]> git.puffer.fish Git - matthieu/frr.git/commitdiff
pimd: Candidate-RP support
authorJafar Al-Gharaibeh <jafar@atcorp.com>
Tue, 23 Jul 2024 05:45:02 +0000 (00:45 -0500)
committerJafar Al-Gharaibeh <jafar@atcorp.com>
Mon, 9 Sep 2024 17:42:28 +0000 (12:42 -0500)
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Signed-off-by: Jafar Al-Gharaibeh <jafar@atcorp.com>
18 files changed:
pimd/pim6_cmd.c
pimd/pim6_main.c
pimd/pim_bsm.c
pimd/pim_bsm.h
pimd/pim_cmd.c
pimd/pim_cmd.h
pimd/pim_cmd_common.c
pimd/pim_cmd_common.h
pimd/pim_iface.c
pimd/pim_main.c
pimd/pim_nb.c
pimd/pim_nb.h
pimd/pim_nb_config.c
pimd/pim_vty.c
pimd/pim_zebra.c
pimd/subdir.am
yang/frr-pim-candidate.yang [new file with mode: 0644]
yang/subdir.am

index f7a4e0e481120c4d41abe3d2de84a231a9b1a9db..838a6d174ef96250c641c2fc717728ee241ca699 100644 (file)
@@ -1259,6 +1259,41 @@ DEFPY (no_ipv6_pim_ucast_bsm,
        return pim_process_no_unicast_bsm_cmd(vty);
 }
 
+DEFPY (pim6_bsr_candidate_rp,
+       pim6_bsr_candidate_rp_cmd,
+       "[no] bsr candidate-rp [{priority (0-255)|interval (1-4294967295)|source <address X:X::X:X|interface IFNAME|loopback$loopback|any$any>}]",
+       NO_STR
+       "Bootstrap Router configuration\n"
+       "Make this router a Candidate RP\n"
+       "RP Priority (lower wins)\n"
+       "RP Priority (lower wins)\n"
+       "Advertisement interval (seconds)\n"
+       "Advertisement interval (seconds)\n"
+       "Specify IP address for RP operation\n"
+       "Local address to use\n"
+       "Local address to use\n"
+       "Interface to pick address from\n"
+       "Interface to pick address from\n"
+       "Pick highest loopback address (default)\n"
+       "Pick highest address from any interface\n")
+{
+       return pim_process_bsr_candidate_cmd(vty, FRR_PIM_CAND_RP_XPATH, no,
+                                            true, any, ifname, address_str,
+                                            priority_str, interval_str);
+}
+
+DEFPY (pim6_bsr_candidate_rp_group,
+       pim6_bsr_candidate_rp_group_cmd,
+       "[no] bsr candidate-rp group X:X::X:X/M",
+       NO_STR
+       "Bootstrap Router configuration\n"
+       "Make this router a Candidate RP\n"
+       "Configure groups to become candidate RP for\n"
+       "Multicast group prefix\n")
+{
+       return pim_process_bsr_crp_grp_cmd(vty, group_str, no);
+}
+
 DEFPY (pim6_ssmpingd,
        pim6_ssmpingd_cmd,
        "ssmpingd [X:X::X:X]$source",
@@ -1719,6 +1754,61 @@ DEFPY (show_ipv6_pim_secondary,
        return pim_show_secondary_helper(vrf, vty);
 }
 
+DEFPY (show_ipv6_pim_cand_rp,
+       show_ipv6_pim_cand_rp_cmd,
+       "show ipv6 pim candidate-rp [vrf VRF_NAME] [json$uj]",
+       SHOW_STR
+       IPV6_STR
+       PIM_STR
+       "PIM Candidate RP state\n"
+       VRF_CMD_HELP_STR
+       JSON_STR)
+{
+       struct vrf *vrf = pim_cmd_lookup(vty, vrf_name);
+       struct pim_instance *pim;
+       struct bsm_scope *scope;
+       json_object *json = NULL;
+
+       if (!vrf || !vrf->info)
+               return CMD_WARNING;
+
+       pim = (struct pim_instance *)vrf->info;
+       scope = &pim->global_scope;
+
+       if (!scope->cand_rp_addrsel.run) {
+               if (uj)
+                       vty_out(vty, "{}\n");
+               else
+                       vty_out(vty,
+                               "This router is not currently operating as Candidate RP\n");
+               return CMD_SUCCESS;
+       }
+
+       if (uj) {
+               json = json_object_new_object();
+               json_object_string_addf(json, "address", "%pPA",
+                                       &scope->cand_rp_addrsel.run_addr);
+               json_object_int_add(json, "priority", scope->cand_rp_prio);
+               json_object_int_add(json, "nextAdvertisementMsec",
+                                   event_timer_remain_msec(
+                                           scope->cand_rp_adv_timer));
+
+               vty_out(vty, "%s\n",
+                       json_object_to_json_string_ext(json,
+                                                      JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
+               return CMD_SUCCESS;
+       }
+
+       vty_out(vty, "Candidate-RP\nAddress:   %pPA\nPriority:  %u\n\n",
+               &scope->cand_rp_addrsel.run_addr, scope->cand_rp_prio);
+       vty_out(vty, "Next adv.: %lu msec\n",
+               event_timer_remain_msec(scope->cand_rp_adv_timer));
+
+
+       return CMD_SUCCESS;
+}
+
 DEFPY (show_ipv6_pim_statistics,
        show_ipv6_pim_statistics_cmd,
        "show ipv6 pim [vrf NAME] statistics [interface WORD$word] [json$json]",
@@ -2650,6 +2740,8 @@ void pim_cmd_init(void)
        install_element(PIM6_NODE, &no_pim6_rp_prefix_list_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);
+       install_element(PIM6_NODE, &pim6_bsr_candidate_rp_group_cmd);
 
        install_element(CONFIG_NODE, &ipv6_mld_group_watermark_cmd);
        install_element(VRF_NODE, &ipv6_mld_group_watermark_cmd);
@@ -2705,6 +2797,7 @@ void pim_cmd_init(void)
        install_element(VIEW_NODE, &show_ipv6_pim_rpf_cmd);
        install_element(VIEW_NODE, &show_ipv6_pim_rpf_vrf_all_cmd);
        install_element(VIEW_NODE, &show_ipv6_pim_secondary_cmd);
+       install_element(VIEW_NODE, &show_ipv6_pim_cand_rp_cmd);
        install_element(VIEW_NODE, &show_ipv6_pim_statistics_cmd);
        install_element(VIEW_NODE, &show_ipv6_pim_upstream_cmd);
        install_element(VIEW_NODE, &show_ipv6_pim_upstream_vrf_all_cmd);
index 24443404eb5eac3f5da8f0e2630440da15eaa88e..07b70ae2b3df72ce691b74d736b5a101f2a1b5b6 100644 (file)
@@ -103,6 +103,7 @@ static const struct frr_yang_module_info *const pim6d_yang_modules[] = {
        &frr_routing_info,
        &frr_pim_info,
        &frr_pim_rp_info,
+       &frr_pim_candidate_info,
        &frr_gmp_info,
 };
 
index 2d451718a98343972a7cf8016bca0a98d74b13a0..c3c44448020294c21ac26a4fbb55b57fbcae812b 100644 (file)
 #include "pim_time.h"
 #include "pim_zebra.h"
 #include "pim_util.h"
+#include "pim_sock.h"
+
+#include <lib/network.h>
+#include <lib/iana_afi.h>
+#include <lib/sockunion.h>
+#include <lib/sockopt.h>
 
 /* Functions forward declaration */
 static void pim_bs_timer_start(struct bsm_scope *scope, int bs_timeout);
@@ -35,6 +41,16 @@ DEFINE_MTYPE_STATIC(PIMD, PIM_BSGRP_NODE, "PIM BSR advertised grp info");
 DEFINE_MTYPE_STATIC(PIMD, PIM_BSRP_INFO, "PIM BSR advertised RP info");
 DEFINE_MTYPE_STATIC(PIMD, PIM_BSM_FRAG, "PIM BSM fragment");
 DEFINE_MTYPE_STATIC(PIMD, PIM_BSM_PKT_VAR_MEM, "PIM BSM Packet");
+DEFINE_MTYPE_STATIC(PIMD, PIM_CAND_RP_GRP, "PIM Candidate RP group");
+
+static int cand_rp_group_cmp(const struct cand_rp_group *a,
+                            const struct cand_rp_group *b)
+{
+       return prefix_cmp(&a->p, &b->p);
+}
+
+DECLARE_RBTREE_UNIQ(cand_rp_groups, struct cand_rp_group, item,
+                   cand_rp_group_cmp);
 
 /* All bsm packets forwarded shall be fit within ip mtu less iphdr(max) */
 #define MAX_IP_HDR_LEN 24
@@ -214,34 +230,57 @@ static inline void pim_bs_timer_restart(struct bsm_scope *scope, int bs_timeout)
 
 void pim_bsm_proc_init(struct pim_instance *pim)
 {
-       memset(&pim->global_scope, 0, sizeof(struct bsm_scope));
-
-       pim->global_scope.sz_id = PIM_GBL_SZ_ID;
-       pim->global_scope.bsrp_table = route_table_init();
-       pim->global_scope.accept_nofwd_bsm = true;
-       pim->global_scope.state = NO_INFO;
-       pim->global_scope.pim = pim;
-       bsm_frags_init(pim->global_scope.bsm_frags);
-       pim_bs_timer_start(&pim->global_scope, PIM_BS_TIME);
+       struct bsm_scope *scope = &pim->global_scope;
+
+       memset(scope, 0, sizeof(*scope));
+
+       scope->sz_id = PIM_GBL_SZ_ID;
+       scope->bsrp_table = route_table_init();
+       scope->accept_nofwd_bsm = true;
+       scope->state = NO_INFO;
+       scope->pim = pim;
+       bsm_frags_init(scope->bsm_frags);
+       pim_bs_timer_start(scope, PIM_BS_TIME);
+
+       scope->cand_rp_interval = PIM_CRP_ADV_INTERVAL;
+       cand_rp_groups_init(scope->cand_rp_groups);
+
+       scope->unicast_sock = pim_socket_raw(IPPROTO_PIM);
+       set_nonblocking(scope->unicast_sock);
+       sockopt_reuseaddr(scope->unicast_sock);
+       setsockopt_ipv6_pktinfo(scope->unicast_sock, 1);
+       pim_socket_ip_hdr(scope->unicast_sock);
+
+       frr_with_privs (&pimd_privs) {
+               vrf_bind(pim->vrf->vrf_id, scope->unicast_sock, NULL);
+       }
 }
 
 void pim_bsm_proc_free(struct pim_instance *pim)
 {
+       struct bsm_scope *scope = &pim->global_scope;
        struct route_node *rn;
        struct bsgrp_node *bsgrp;
+       struct cand_rp_group *crpgrp;
 
-       pim_bs_timer_stop(&pim->global_scope);
-       pim_bsm_frags_free(&pim->global_scope);
+       close(scope->unicast_sock);
 
-       for (rn = route_top(pim->global_scope.bsrp_table); rn;
-            rn = route_next(rn)) {
+       pim_bs_timer_stop(scope);
+       pim_bsm_frags_free(scope);
+
+       for (rn = route_top(scope->bsrp_table); rn; rn = route_next(rn)) {
                bsgrp = rn->info;
                if (!bsgrp)
                        continue;
                pim_free_bsgrp_data(bsgrp);
        }
 
-       route_table_finish(pim->global_scope.bsrp_table);
+       while ((crpgrp = cand_rp_groups_pop(scope->cand_rp_groups)))
+               XFREE(MTYPE_PIM_CAND_RP_GRP, crpgrp);
+
+       cand_rp_groups_fini(scope->cand_rp_groups);
+
+       route_table_finish(scope->bsrp_table);
 }
 
 static bool is_hold_time_elapsed(void *data)
@@ -538,6 +577,8 @@ static void pim_bsm_update(struct pim_instance *pim, pim_addr bsr,
                pim->global_scope.current_bsr_first_ts =
                        pim_time_monotonic_sec();
                pim->global_scope.state = ACCEPT_PREFERRED;
+
+               pim_cand_rp_trigger(&pim->global_scope);
        }
        pim->global_scope.current_bsr_prio = bsr_prio;
        pim->global_scope.current_bsr_last_ts = pim_time_monotonic_sec();
@@ -1452,6 +1493,366 @@ int pim_bsm_process(struct interface *ifp, pim_sgaddr *sg, uint8_t *buf,
        return 0;
 }
 
+static inline pim_addr if_highest_addr(pim_addr cur, struct interface *ifp)
+{
+       struct connected *connected;
+
+       frr_each (if_connected, ifp->connected, connected) {
+               pim_addr conn_addr;
+
+               if (connected->address->family != PIM_AF)
+                       continue;
+
+               conn_addr = pim_addr_from_prefix(connected->address);
+               /* highest address */
+               if (pim_addr_cmp(conn_addr, cur) > 0)
+                       cur = conn_addr;
+       }
+       return cur;
+}
+
+static void cand_addrsel_clear(struct cand_addrsel *asel)
+{
+       asel->run = false;
+       asel->run_addr = PIMADDR_ANY;
+}
+
+/* returns whether address or active changed */
+static bool cand_addrsel_update(struct cand_addrsel *asel, struct vrf *vrf)
+{
+       bool is_any = false, prev_run = asel->run;
+       struct interface *ifp = NULL;
+       pim_addr new_addr = PIMADDR_ANY;
+
+       if (!asel->cfg_enable)
+               goto out_disable;
+
+       switch (asel->cfg_mode) {
+       case CAND_ADDR_EXPLICIT:
+               new_addr = asel->cfg_addr;
+               ifp = if_lookup_address_local(&asel->cfg_addr, PIM_AF,
+                                             vrf->vrf_id);
+               break;
+
+       case CAND_ADDR_IFACE:
+               ifp = if_lookup_by_name_vrf(asel->cfg_ifname, vrf);
+
+               if (ifp)
+                       new_addr = if_highest_addr(PIMADDR_ANY, ifp);
+               break;
+
+       case CAND_ADDR_ANY:
+               is_any = true;
+               /* fallthru */
+       case CAND_ADDR_LO:
+               FOR_ALL_INTERFACES (vrf, ifp) {
+                       if (!if_is_up(ifp))
+                               continue;
+                       if (is_any || if_is_loopback(ifp) || if_is_vrf(ifp))
+                               new_addr = if_highest_addr(new_addr, ifp);
+               }
+               break;
+       }
+
+       if (ifp && !if_is_up(ifp))
+               goto out_disable;
+
+       if (pim_addr_is_any(new_addr))
+               goto out_disable;
+
+       /* nothing changed re. address (don't care about interface changes) */
+       if (asel->run && !pim_addr_cmp(asel->run_addr, new_addr))
+               return !prev_run;
+
+       asel->run = true;
+       asel->run_addr = new_addr;
+       return true;
+
+out_disable:
+       asel->run = false;
+       asel->run_addr = PIMADDR_ANY;
+
+       return prev_run;
+}
+
+static void pim_cand_rp_adv_stop_maybe(struct bsm_scope *scope)
+{
+       /* actual check whether stop should be sent - covers address
+        * changes as well as run_addr = 0.0.0.0 (C-RP shutdown)
+        */
+       if (pim_addr_is_any(scope->cand_rp_prev_addr) ||
+           !pim_addr_cmp(scope->cand_rp_prev_addr,
+                         scope->cand_rp_addrsel.run_addr))
+               return;
+
+       switch (scope->state) {
+       case ACCEPT_PREFERRED:
+               /* TBD: BSR_ELECTED */
+               break;
+
+       case NO_INFO:
+       case ACCEPT_ANY:
+       default:
+               return;
+       }
+
+       if (PIM_DEBUG_BSM)
+               zlog_debug("Candidate-RP (-, %pPA) deregistering self to %pPA",
+                          &scope->cand_rp_prev_addr, &scope->current_bsr);
+
+       struct cand_rp_msg *msg;
+       uint8_t buf[PIM_MSG_HEADER_LEN + sizeof(*msg) + sizeof(pim_encoded_group)];
+
+       msg = (struct cand_rp_msg *)(&buf[PIM_MSG_HEADER_LEN]);
+       msg->prefix_cnt = 0;
+       msg->rp_prio = 255;
+       msg->rp_holdtime = 0;
+       msg->rp_addr.family = PIM_IANA_AFI;
+       msg->rp_addr.reserved = 0;
+       msg->rp_addr.addr = scope->cand_rp_prev_addr;
+
+       pim_msg_build_header(PIMADDR_ANY, scope->current_bsr, buf, sizeof(buf),
+                            PIM_MSG_TYPE_CANDIDATE, false);
+
+       if (pim_msg_send(scope->unicast_sock, PIMADDR_ANY, scope->current_bsr,
+                        buf, sizeof(buf), NULL)) {
+               zlog_warn("failed to send Cand-RP message: %m");
+       }
+
+       scope->cand_rp_prev_addr = PIMADDR_ANY;
+}
+
+static void pim_cand_rp_adv(struct event *t)
+{
+       struct bsm_scope *scope = EVENT_ARG(t);
+       int next_msec;
+
+       pim_cand_rp_adv_stop_maybe(scope);
+
+       if (!scope->cand_rp_addrsel.run) {
+               scope->cand_rp_adv_trigger = 0;
+               return;
+       }
+
+       switch (scope->state) {
+       case ACCEPT_PREFERRED:
+               /* TBD: BSR_ELECTED */
+               break;
+
+       case ACCEPT_ANY:
+       case NO_INFO:
+       default:
+               /* state change will retrigger */
+               scope->cand_rp_adv_trigger = 0;
+
+               zlog_warn("Candidate-RP advertisement not sent in state %d",
+                         scope->state);
+               return;
+       }
+
+       if (PIM_DEBUG_BSM)
+               zlog_debug("Candidate-RP (%u, %pPA) advertising %zu groups to %pPA",
+                          scope->cand_rp_prio, &scope->cand_rp_addrsel.run_addr,
+                          cand_rp_groups_count(scope->cand_rp_groups),
+                          &scope->current_bsr);
+
+       struct cand_rp_group *grp;
+       struct cand_rp_msg *msg;
+       uint8_t buf[PIM_MSG_HEADER_LEN + sizeof(*msg) +
+                   sizeof(pim_encoded_group) *
+                           cand_rp_groups_count(scope->cand_rp_groups)];
+       size_t i = 0;
+
+
+       msg = (struct cand_rp_msg *)(&buf[PIM_MSG_HEADER_LEN]);
+       msg->prefix_cnt = cand_rp_groups_count(scope->cand_rp_groups);
+       msg->rp_prio = scope->cand_rp_prio;
+       msg->rp_holdtime =
+               htons(MAX(151, (scope->cand_rp_interval * 5 + 1) / 2));
+       msg->rp_addr.family = PIM_IANA_AFI;
+       msg->rp_addr.reserved = 0;
+       msg->rp_addr.addr = scope->cand_rp_addrsel.run_addr;
+
+       frr_each (cand_rp_groups, scope->cand_rp_groups, grp) {
+               memset(&msg->groups[i], 0, sizeof(msg->groups[i]));
+
+               msg->groups[i].family = PIM_IANA_AFI;
+               msg->groups[i].mask = grp->p.prefixlen;
+               msg->groups[i].addr = grp->p.prefix;
+               i++;
+       }
+
+       scope->cand_rp_prev_addr = scope->cand_rp_addrsel.run_addr;
+
+       pim_msg_build_header(scope->cand_rp_addrsel.run_addr, scope->current_bsr,
+                            buf, sizeof(buf), PIM_MSG_TYPE_CANDIDATE, false);
+
+       if (pim_msg_send(scope->unicast_sock, scope->cand_rp_addrsel.run_addr,
+                        scope->current_bsr, buf, sizeof(buf), NULL)) {
+               zlog_warn("failed to send Cand-RP message: %m");
+       }
+
+       /* -1s...+1s */
+       next_msec = (frr_weak_random() & 2047) - 1024;
+
+       if (scope->cand_rp_adv_trigger) {
+               scope->cand_rp_adv_trigger--;
+               next_msec += 2000;
+       } else
+               next_msec += scope->cand_rp_interval * 1000;
+
+       event_add_timer_msec(router->master, pim_cand_rp_adv, scope, next_msec,
+                            &scope->cand_rp_adv_timer);
+}
+
+void pim_cand_rp_trigger(struct bsm_scope *scope)
+{
+       if (scope->cand_rp_adv_trigger && scope->cand_rp_addrsel.run) {
+               scope->cand_rp_adv_trigger = PIM_CRP_ADV_TRIGCOUNT;
+
+               /* already scheduled to send triggered advertisements, don't
+                * reschedule so burst changes don't result in an advertisement
+                * burst
+                */
+               return;
+       }
+
+       EVENT_OFF(scope->cand_rp_adv_timer);
+
+       if (!scope->cand_rp_addrsel.run)
+               return;
+
+       scope->cand_rp_adv_trigger = PIM_CRP_ADV_TRIGCOUNT;
+
+       struct event t;
+
+       t.arg = scope;
+       pim_cand_rp_adv(&t);
+}
+
+void pim_cand_rp_apply(struct bsm_scope *scope)
+{
+       if (!cand_addrsel_update(&scope->cand_rp_addrsel, scope->pim->vrf))
+               return;
+
+       if (!scope->cand_rp_addrsel.run) {
+               if (PIM_DEBUG_BSM)
+                       zlog_debug("Candidate RP ceasing operation");
+
+               cand_addrsel_clear(&scope->cand_rp_addrsel);
+               EVENT_OFF(scope->cand_rp_adv_timer);
+               pim_cand_rp_adv_stop_maybe(scope);
+               scope->cand_rp_adv_trigger = 0;
+               return;
+       }
+
+       if (PIM_DEBUG_BSM)
+               zlog_debug("Candidate RP: %pPA, priority %u",
+                          &scope->cand_rp_addrsel.run_addr,
+                          scope->cand_rp_prio);
+
+       pim_cand_rp_trigger(scope);
+}
+
+void pim_cand_rp_grp_add(struct bsm_scope *scope, const prefix_pim *p)
+{
+       struct cand_rp_group *grp, ref;
+
+       ref.p = *p;
+       grp = cand_rp_groups_find(scope->cand_rp_groups, &ref);
+       if (grp)
+               return;
+
+       grp = XCALLOC(MTYPE_PIM_CAND_RP_GRP, sizeof(*grp));
+       grp->p = *p;
+       cand_rp_groups_add(scope->cand_rp_groups, grp);
+
+       pim_cand_rp_trigger(scope);
+}
+
+void pim_cand_rp_grp_del(struct bsm_scope *scope, const prefix_pim *p)
+{
+       struct cand_rp_group *grp, ref;
+
+       ref.p = *p;
+       grp = cand_rp_groups_find(scope->cand_rp_groups, &ref);
+       if (!grp)
+               return;
+
+       cand_rp_groups_del(scope->cand_rp_groups, grp);
+       XFREE(MTYPE_PIM_CAND_RP_GRP, grp);
+
+       pim_cand_rp_trigger(scope);
+}
+
+static struct event *t_cand_addrs_reapply;
+
+static void pim_cand_addrs_reapply(struct event *t)
+{
+       struct vrf *vrf;
+
+       RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+               struct pim_instance *pi = vrf->info;
+
+               if (!pi)
+                       continue;
+
+               /* this calls cand_addrsel_update() and applies changes */
+               pim_cand_rp_apply(&pi->global_scope);
+       }
+}
+
+void pim_cand_addrs_changed(void)
+{
+       EVENT_OFF(t_cand_addrs_reapply);
+       event_add_timer_msec(router->master, pim_cand_addrs_reapply, NULL, 1,
+                            &t_cand_addrs_reapply);
+}
+
+static void cand_addrsel_config_write(struct vty *vty,
+                                     struct cand_addrsel *addrsel)
+{
+       switch (addrsel->cfg_mode) {
+       case CAND_ADDR_LO:
+               break;
+       case CAND_ADDR_ANY:
+               vty_out(vty, " source any");
+               break;
+       case CAND_ADDR_IFACE:
+               vty_out(vty, " source interface %s", addrsel->cfg_ifname);
+               break;
+       case CAND_ADDR_EXPLICIT:
+               vty_out(vty, " source address %pPA", &addrsel->cfg_addr);
+               break;
+       }
+}
+
+int pim_cand_config_write(struct pim_instance *pim, struct vty *vty)
+{
+       struct bsm_scope *scope = &pim->global_scope;
+       int ret = 0;
+
+       if (scope->cand_rp_addrsel.cfg_enable) {
+               vty_out(vty, " bsr candidate-rp");
+               if (scope->cand_rp_prio != 192)
+                       vty_out(vty, " priority %u", scope->cand_rp_prio);
+               if (scope->cand_rp_interval != PIM_CRP_ADV_INTERVAL)
+                       vty_out(vty, " interval %u", scope->cand_rp_interval);
+               cand_addrsel_config_write(vty, &scope->cand_rp_addrsel);
+               vty_out(vty, "\n");
+               ret++;
+
+               struct cand_rp_group *group;
+
+               frr_each (cand_rp_groups, scope->cand_rp_groups, group) {
+                       vty_out(vty, " bsr candidate-rp group %pFX\n",
+                               &group->p);
+                       ret++;
+               }
+       }
+       return ret;
+}
+
 void pim_crp_nht_update(struct pim_instance *pim, struct pim_nexthop_cache *pnc)
 {
        /* stub for Candidate-RP */
index fb09e3b1cc3684e43d38ebad5021b5889fb3b064..27714ab459c18c1a3aeb367088fe248abd2bfdd2 100644 (file)
 #define PIM_BS_TIME 60             /* RFC 5059 - Sec 5 */
 #define PIM_BSR_DEFAULT_TIMEOUT 130 /* RFC 5059 - Sec 5 */
 
+#define PIM_CRP_ADV_TRIGCOUNT 3
+#define PIM_CRP_ADV_INTERVAL  60
+#define PIM_CRP_HOLDTIME      150
+
 /* These structures are only encoded IPv4 specific */
 #define PIM_BSM_HDR_LEN sizeof(struct bsm_hdr)
 #define PIM_BSM_GRP_LEN sizeof(struct bsmmsg_grpinfo)
@@ -40,7 +44,31 @@ enum ncbsr_state {
        ACCEPT_PREFERRED
 };
 
+enum cand_addr {
+       CAND_ADDR_LO = 0,
+       CAND_ADDR_ANY,
+       CAND_ADDR_IFACE,
+       CAND_ADDR_EXPLICIT,
+};
+
+/* used separately for Cand-RP, and (TBD) Cand-BSR */
+struct cand_addrsel {
+       bool cfg_enable;
+       enum cand_addr cfg_mode : 8;
+
+       /* only valid for mode==CAND_ADDR_IFACE */
+       char cfg_ifname[IFNAMSIZ];
+       /* only valid for mode==CAND_ADDR_EXPLICIT */
+       pim_addr cfg_addr;
+
+       /* running state updated based on above on zebra events */
+       pim_addr run_addr;
+       bool run;
+};
+
+
 PREDECL_DLIST(bsm_frags);
+PREDECL_RBTREE_UNIQ(cand_rp_groups);
 
 /* BSM scope - bsm processing is per scope */
 struct bsm_scope {
@@ -60,6 +88,27 @@ struct bsm_scope {
 
        struct route_table *bsrp_table; /* group2rp mapping rcvd from BSR */
        struct event *bs_timer;         /* Boot strap timer */
+
+       /* Candidate RP config */
+       struct cand_addrsel cand_rp_addrsel;
+       uint8_t cand_rp_prio;
+       unsigned int cand_rp_interval; /* default: PIM_CRP_ADV_INTERVAL=60 */
+       /* holdtime is not configurable, always 2.5 * interval. */
+       struct cand_rp_groups_head cand_rp_groups[1];
+
+       /* Candidate RP state */
+       int unicast_sock;
+       struct event *cand_rp_adv_timer;
+       unsigned int cand_rp_adv_trigger; /* # trigg. C-RP-Adv left to send */
+
+       /* for sending holdtime=0 zap */
+       pim_addr cand_rp_prev_addr;
+};
+
+struct cand_rp_group {
+       struct cand_rp_groups_item item;
+
+       prefix_pim p;
 };
 
 /* BSM packet (= fragment) - this is stored as list in bsm_frags inside scope
@@ -200,6 +249,14 @@ struct bsmmsg_rpinfo {
        uint8_t reserved;
 } __attribute__((packed));
 
+struct cand_rp_msg {
+       uint8_t prefix_cnt;
+       uint8_t rp_prio;
+       uint16_t rp_holdtime;
+       pim_encoded_unicast rp_addr;
+       pim_encoded_group groups[0];
+} __attribute__((packed));
+
 /* API */
 void pim_bsm_proc_init(struct pim_instance *pim);
 void pim_bsm_proc_free(struct pim_instance *pim);
@@ -210,4 +267,14 @@ int pim_bsm_process(struct interface *ifp, pim_sgaddr *sg, uint8_t *buf,
 bool pim_bsm_new_nbr_fwd(struct pim_neighbor *neigh, struct interface *ifp);
 struct bsgrp_node *pim_bsm_get_bsgrp_node(struct bsm_scope *scope,
                                          struct prefix *grp);
+
+void pim_cand_rp_apply(struct bsm_scope *scope);
+void pim_cand_rp_trigger(struct bsm_scope *scope);
+void pim_cand_rp_grp_add(struct bsm_scope *scope, const prefix_pim *p);
+void pim_cand_rp_grp_del(struct bsm_scope *scope, const prefix_pim *p);
+
+void pim_cand_addrs_changed(void);
+
+int pim_cand_config_write(struct pim_instance *pim, struct vty *vty);
+
 #endif
index 633c46966ead1b5f1defdc853feff4827dc5a193..88c9a4b226cf2f9a42f0b45f77637a46b386d670 100644 (file)
@@ -2877,6 +2877,66 @@ DEFPY (show_ip_pim_bsrp,
        return pim_show_group_rp_mappings_info_helper(vrf, vty, !!json);
 }
 
+DEFUN (show_ip_pim_cand_rp,
+       show_ip_pim_cand_rp_cmd,
+       "show ip pim candidate-rp [vrf NAME] [json]",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM Candidate RP state\n"
+       VRF_CMD_HELP_STR
+       JSON_STR)
+{
+       bool uj = use_json(argc, argv);
+       int idx = 2;
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
+       struct pim_instance *pim;
+       struct bsm_scope *scope;
+       json_object *json = NULL;
+
+       if (!vrf || !vrf->info)
+               return CMD_WARNING;
+
+       pim = (struct pim_instance *)vrf->info;
+       scope = &pim->global_scope;
+
+       if (!scope->cand_rp_addrsel.run) {
+               if (uj)
+                       vty_out(vty, "{}\n");
+               else
+                       vty_out(vty,
+                               "This router is not currently operating as Candidate RP\n");
+               return CMD_SUCCESS;
+       }
+
+       if (uj) {
+               char buf[INET_ADDRSTRLEN];
+
+               json = json_object_new_object();
+               inet_ntop(AF_INET, &scope->cand_rp_addrsel.run_addr, buf,
+                         sizeof(buf));
+               json_object_string_add(json, "address", buf);
+               json_object_int_add(json, "priority", scope->cand_rp_prio);
+               json_object_int_add(json, "nextAdvertisementMsec",
+                                   pim_time_timer_remain_msec(
+                                           scope->cand_rp_adv_timer));
+
+               vty_out(vty, "%s\n",
+                       json_object_to_json_string_ext(json,
+                                                      JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
+               return CMD_SUCCESS;
+       }
+
+       vty_out(vty, "Candidate-RP\nAddress:   %pI4\nPriority:  %u\n\n",
+               &scope->cand_rp_addrsel.run_addr, scope->cand_rp_prio);
+       vty_out(vty, "Next adv.: %lu msec\n",
+               pim_time_timer_remain_msec(scope->cand_rp_adv_timer));
+
+
+       return CMD_SUCCESS;
+}
+
 DEFPY (show_ip_pim_statistics,
        show_ip_pim_statistics_cmd,
        "show ip pim [vrf NAME] statistics [interface WORD$word] [json$json]",
@@ -4376,6 +4436,41 @@ DEFPY_ATTR(no_ip_pim_rp_prefix_list,
        return ret;
 }
 
+DEFPY (pim_bsr_candidate_rp,
+       pim_bsr_candidate_rp_cmd,
+       "[no] bsr candidate-rp [{priority (0-255)|interval (1-4294967295)|source <address A.B.C.D|interface IFNAME|loopback$loopback|any$any>}]",
+       NO_STR
+       BSR_STR
+       "Make this router a Candidate RP\n"
+       "RP Priority (lower wins)\n"
+       "RP Priority (lower wins)\n"
+       "Advertisement interval (seconds)\n"
+       "Advertisement interval (seconds)\n"
+       "Specify IP address for RP operation\n"
+       "Local address to use\n"
+       "Local address to use\n"
+       "Interface to pick address from\n"
+       "Interface to pick address from\n"
+       "Pick highest loopback address (default)\n"
+       "Pick highest address from any interface\n")
+{
+       return pim_process_bsr_candidate_cmd(vty, FRR_PIM_CAND_RP_XPATH, no,
+                                            true, any, ifname, address_str,
+                                            priority_str, interval_str);
+}
+
+DEFPY (pim_bsr_candidate_rp_group,
+       pim_bsr_candidate_rp_group_cmd,
+       "[no] bsr candidate-rp group A.B.C.D/M",
+       NO_STR
+       BSR_STR
+       "Make this router a Candidate RP\n"
+       "Configure groups to become candidate RP for\n"
+       "Multicast group prefix\n")
+{
+       return pim_process_bsr_crp_grp_cmd(vty, group_str, no);
+}
+
 DEFPY (pim_ssm_prefix_list,
        pim_ssm_prefix_list_cmd,
        "ssm prefix-list PREFIXLIST4_NAME$plist",
@@ -8550,6 +8645,9 @@ void pim_cmd_init(void)
        install_element(PIM_NODE, &no_pim_msdp_mesh_group_source_cmd);
        install_element(PIM_NODE, &no_pim_msdp_mesh_group_cmd);
 
+       install_element(PIM_NODE, &pim_bsr_candidate_rp_cmd);
+       install_element(PIM_NODE, &pim_bsr_candidate_rp_group_cmd);
+
        install_element(INTERFACE_NODE, &interface_ip_igmp_cmd);
        install_element(INTERFACE_NODE, &interface_no_ip_igmp_cmd);
        install_element(INTERFACE_NODE, &interface_ip_igmp_join_cmd);
@@ -8670,6 +8768,7 @@ void pim_cmd_init(void)
        install_element(VIEW_NODE, &show_ip_pim_nexthop_lookup_cmd);
        install_element(VIEW_NODE, &show_ip_pim_bsrp_cmd);
        install_element(VIEW_NODE, &show_ip_pim_bsm_db_cmd);
+       install_element(VIEW_NODE, &show_ip_pim_cand_rp_cmd);
        install_element(VIEW_NODE, &show_ip_pim_statistics_cmd);
        install_element(VIEW_NODE, &show_ip_msdp_peer_detail_cmd);
        install_element(VIEW_NODE, &show_ip_msdp_peer_detail_vrf_all_cmd);
index d39d77cd2fde34fc1332c03468362328bb7295b7..89ca77dca206d7385421fa9aa27d2d7a5f0028c5 100644 (file)
@@ -24,6 +24,7 @@
 #define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR "IGMP max query response value (deciseconds)\n"
 #define IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR   "IGMP last member query interval\n"
 #define IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR      "IGMP last member query count\n"
+#define BSR_STR                                     "Bootstrap Router configuration\n"
 #define DEBUG_IGMP_STR                              "IGMP protocol activity\n"
 #define DEBUG_IGMP_EVENTS_STR                       "IGMP protocol events\n"
 #define DEBUG_IGMP_PACKETS_STR                      "IGMP protocol packets\n"
index a87f5b6981842c03ef7157353c678168893086ca..0ab2b841d6faeaf9eee93fc483a95c9067230110 100644 (file)
@@ -3389,6 +3389,55 @@ int pim_process_no_unicast_bsm_cmd(struct vty *vty)
                                    FRR_PIM_AF_XPATH_VAL);
 }
 
+/* helper for bsr/rp candidate commands*/
+int pim_process_bsr_candidate_cmd(struct vty *vty, const char *cand_str,
+                                 bool no, bool is_rp, bool any,
+                                 const char *ifname, const char *addr,
+                                 const char *prio, const char *interval)
+{
+       if (no)
+               nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
+       else {
+               nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
+
+               if (any)
+                       nb_cli_enqueue_change(vty, "./if-any", NB_OP_CREATE,
+                                             NULL);
+               else if (ifname)
+                       nb_cli_enqueue_change(vty, "./interface", NB_OP_CREATE,
+                                             ifname);
+               else if (addr)
+                       nb_cli_enqueue_change(vty, "./address", NB_OP_CREATE,
+                                             addr);
+               else
+                       nb_cli_enqueue_change(vty, "./if-loopback",
+                                             NB_OP_CREATE, NULL);
+
+               if (prio)
+                       nb_cli_enqueue_change(vty,
+                                             (is_rp ? "./rp-priority"
+                                                    : "./bsr-priority"),
+                                             NB_OP_MODIFY, prio);
+
+               /* only valid for rp candidate case*/
+               if (is_rp && interval)
+                       nb_cli_enqueue_change(vty, "./advertisement-interval",
+                                             NB_OP_MODIFY, interval);
+       }
+
+       return nb_cli_apply_changes(vty, "%s", cand_str);
+}
+
+int pim_process_bsr_crp_grp_cmd(struct vty *vty, const char *grp, bool no)
+{
+       if (no)
+               nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, grp);
+       else
+               nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, grp);
+
+       return nb_cli_apply_changes(vty, "%s/group-list", FRR_PIM_CAND_RP_XPATH);
+}
+
 static void show_scan_oil_stats(struct pim_instance *pim, struct vty *vty,
                                time_t now)
 {
index da2e44be585b914796f0295cd8c3d681b1aacc63..6ba126ef8eb39ce218ad2184c8126248b93cc933 100644 (file)
@@ -53,6 +53,13 @@ int pim_process_bsm_cmd(struct vty *vty);
 int pim_process_no_bsm_cmd(struct vty *vty);
 int pim_process_unicast_bsm_cmd(struct vty *vty);
 int pim_process_no_unicast_bsm_cmd(struct vty *vty);
+
+int pim_process_bsr_candidate_cmd(struct vty *vty, const char *cand_str,
+                                 bool no, bool is_rp, bool any,
+                                 const char *ifname, const char *addr,
+                                 const char *prio, const char *interval);
+int pim_process_bsr_crp_grp_cmd(struct vty *vty, const char *grp, bool no);
+
 void json_object_pim_upstream_add(json_object *json, struct pim_upstream *up);
 void pim_show_rpf(struct pim_instance *pim, struct vty *vty, json_object *json);
 void pim_show_neighbors_secondary(struct pim_instance *pim, struct vty *vty);
index 45a2435ae5509b9df35a3e08e7400fd3e2d45f1c..125d35ac46d5f9a39641996945f6cdc1f62015e7 100644 (file)
@@ -1844,6 +1844,8 @@ static int pim_ifp_up(struct interface *ifp)
                        }
                }
        }
+
+       pim_cand_addrs_changed();
        return 0;
 }
 
@@ -1880,6 +1882,7 @@ static int pim_ifp_down(struct interface *ifp)
                pim_ifstat_reset(ifp);
        }
 
+       pim_cand_addrs_changed();
        return 0;
 }
 
index 8f2ce0bed335aaeace5e7d6c8fc927bde0c70500..f88aca719eda0647ca136aae30380d640a48c85d 100644 (file)
@@ -68,6 +68,7 @@ static const struct frr_yang_module_info *const pimd_yang_modules[] = {
        &frr_routing_info,
        &frr_pim_info,
        &frr_pim_rp_info,
+       &frr_pim_candidate_info,
        &frr_gmp_info,
 };
 
index c154c18afa8f404d88f4fea01f4ef513c6b5bb39..53e7147d53d7c71d8cc438940cc5fc6565282d60 100644 (file)
@@ -385,6 +385,70 @@ const struct frr_yang_module_info frr_pim_rp_info = {
        }
 };
 
+const struct frr_yang_module_info frr_pim_candidate_info = {
+       .name = "frr-pim-candidate",
+       .nodes = {
+               /* Candidate-RP */
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-rp",
+                       .cbs = {
+                               .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_create,
+                               .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_destroy,
+                       }
+               },
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-rp/rp-priority",
+                       .cbs = {
+                               .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_priority_modify,
+                       }
+               },
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-rp/advertisement-interval",
+                       .cbs = {
+                               .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_adv_interval_modify,
+                       }
+               },
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-rp/group-list",
+                       .cbs = {
+                               .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_group_list_create,
+                               .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_group_list_destroy,
+                       }
+               },
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-rp/address",
+                       .cbs = {
+                               .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_modify,
+                               .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_destroy,
+                       }
+               },
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-rp/interface",
+                       .cbs = {
+                               .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_modify,
+                               .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_destroy,
+                       }
+               },
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-rp/if-loopback",
+                       .cbs = {
+                               .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_create,
+                               .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_destroy,
+                       }
+               },
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-rp/if-any",
+                       .cbs = {
+                               .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_create,
+                               .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_destroy,
+                       }
+               },
+               {
+                       .xpath = NULL,
+               },
+       }
+};
+
 /* clang-format off */
 const struct frr_yang_module_info frr_gmp_info = {
        .name = "frr-gmp",
index fc4c11cea94da25d02d6fbb37d7e9e0b08f8187b..024c4b6c9d60021a155b333736c2b3a72386182a 100644 (file)
@@ -9,6 +9,7 @@
 
 extern const struct frr_yang_module_info frr_pim_info;
 extern const struct frr_yang_module_info frr_pim_rp_info;
+extern const struct frr_yang_module_info frr_pim_candidate_info;
 extern const struct frr_yang_module_info frr_gmp_info;
 
 /* frr-pim prototypes*/
@@ -159,6 +160,26 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp
 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);
 
+/* frr-candidate */
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_create(
+       struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_destroy(
+       struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_priority_modify(
+       struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_adv_interval_modify(
+       struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_group_list_create(
+       struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_group_list_destroy(
+       struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_create(
+       struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_modify(
+       struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_destroy(
+       struct nb_cb_destroy_args *args);
+
 /* frr-gmp prototypes*/
 int lib_interface_gmp_address_family_create(
        struct nb_cb_create_args *args);
@@ -219,6 +240,8 @@ 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_CAND_RP_XPATH                                           \
+       "./frr-pim-candidate:candidate-rp"
 #define FRR_GMP_INTERFACE_XPATH                                         \
        "./frr-gmp:gmp/address-family[address-family='%s']"
 #define FRR_GMP_ENABLE_XPATH                                            \
index 037bfea7861ab71ddf6f09554407830110f6e6ea..0faddef50a79adb1361dfe5dacf919c3091047e2 100644 (file)
@@ -2671,6 +2671,245 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp
        return NB_OK;
 }
 
+static void yang_addrsel(struct cand_addrsel *addrsel,
+                        const struct lyd_node *node)
+{
+       memset(addrsel->cfg_ifname, 0, sizeof(addrsel->cfg_ifname));
+       addrsel->cfg_addr = PIMADDR_ANY;
+
+       if (yang_dnode_exists(node, "if-any")) {
+               addrsel->cfg_mode = CAND_ADDR_ANY;
+       } else if (yang_dnode_exists(node, "address")) {
+               addrsel->cfg_mode = CAND_ADDR_EXPLICIT;
+               yang_dnode_get_pimaddr(&addrsel->cfg_addr, node, "address");
+       } else if (yang_dnode_exists(node, "interface")) {
+               addrsel->cfg_mode = CAND_ADDR_IFACE;
+               strlcpy(addrsel->cfg_ifname,
+                       yang_dnode_get_string(node, "interface"),
+                       sizeof(addrsel->cfg_ifname));
+       } else if (yang_dnode_exists(node, "if-loopback")) {
+               addrsel->cfg_mode = CAND_ADDR_LO;
+       }
+}
+
+static int candidate_rp_addrsel(struct bsm_scope *scope,
+                               const struct lyd_node *cand_rp_node)
+{
+       yang_addrsel(&scope->cand_rp_addrsel, cand_rp_node);
+       pim_cand_rp_apply(scope);
+       return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_create(
+       struct nb_cb_create_args *args)
+{
+       struct vrf *vrf;
+       struct pim_instance *pim;
+       struct bsm_scope *scope;
+
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               vrf = nb_running_get_entry(args->dnode, NULL, true);
+               pim = vrf->info;
+               scope = &pim->global_scope;
+
+               scope->cand_rp_addrsel.cfg_enable = true;
+               scope->cand_rp_prio = yang_dnode_get_uint8(args->dnode,
+                                                          "rp-priority");
+               scope->cand_rp_interval =
+                       yang_dnode_get_uint32(args->dnode,
+                                             "advertisement-interval");
+
+               candidate_rp_addrsel(scope, args->dnode);
+               break;
+       }
+
+       return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       struct vrf *vrf;
+       struct pim_instance *pim;
+       struct bsm_scope *scope;
+
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               vrf = nb_running_get_entry(args->dnode, NULL, true);
+               pim = vrf->info;
+               scope = &pim->global_scope;
+
+               scope->cand_rp_addrsel.cfg_enable = false;
+
+               pim_cand_rp_apply(scope);
+               break;
+       }
+
+       return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_priority_modify(
+       struct nb_cb_modify_args *args)
+{
+       struct vrf *vrf;
+       struct pim_instance *pim;
+       struct bsm_scope *scope;
+
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               vrf = nb_running_get_entry(args->dnode, NULL, true);
+               pim = vrf->info;
+               scope = &pim->global_scope;
+
+               scope->cand_rp_prio = yang_dnode_get_uint8(args->dnode, NULL);
+
+               pim_cand_rp_trigger(scope);
+               break;
+       }
+
+       return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_adv_interval_modify(
+       struct nb_cb_modify_args *args)
+{
+       struct vrf *vrf;
+       struct pim_instance *pim;
+       struct bsm_scope *scope;
+
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               vrf = nb_running_get_entry(args->dnode, NULL, true);
+               pim = vrf->info;
+               scope = &pim->global_scope;
+
+               scope->cand_rp_interval = yang_dnode_get_uint32(args->dnode,
+                                                               NULL);
+
+               pim_cand_rp_trigger(scope);
+               break;
+       }
+
+       return NB_OK;
+}
+
+#if PIM_IPV == 4
+#define yang_dnode_get_pim_p yang_dnode_get_ipv4p
+#else
+#define yang_dnode_get_pim_p yang_dnode_get_ipv6p
+#endif
+
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_group_list_create(
+       struct nb_cb_create_args *args)
+{
+       struct vrf *vrf;
+       struct pim_instance *pim;
+       struct bsm_scope *scope;
+       prefix_pim p;
+
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               vrf = nb_running_get_entry(args->dnode, NULL, true);
+               pim = vrf->info;
+               scope = &pim->global_scope;
+
+               yang_dnode_get_pim_p(&p, args->dnode, ".");
+               pim_cand_rp_grp_add(scope, &p);
+               break;
+       }
+       return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_group_list_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       struct vrf *vrf;
+       struct pim_instance *pim;
+       struct bsm_scope *scope;
+       prefix_pim p;
+
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               vrf = nb_running_get_entry(args->dnode, NULL, true);
+               pim = vrf->info;
+               scope = &pim->global_scope;
+
+               yang_dnode_get_pim_p(&p, args->dnode, ".");
+               pim_cand_rp_grp_del(scope, &p);
+               break;
+       }
+       return NB_OK;
+}
+
+static int candidate_rp_addrsel_common(enum nb_event event,
+                                      const struct lyd_node *dnode)
+{
+       struct vrf *vrf;
+       struct pim_instance *pim;
+       struct bsm_scope *scope;
+
+       dnode = lyd_parent(dnode);
+
+       switch (event) {
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               vrf = nb_running_get_entry(dnode, NULL, true);
+               pim = vrf->info;
+               scope = &pim->global_scope;
+
+               candidate_rp_addrsel(scope, dnode);
+               break;
+       }
+       return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_create(
+       struct nb_cb_create_args *args)
+{
+       return candidate_rp_addrsel_common(args->event, args->dnode);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_modify(
+       struct nb_cb_modify_args *args)
+{
+       return candidate_rp_addrsel_common(args->event, args->dnode);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_rp_addrsel_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       /* nothing to do here - we'll get a create or modify event too */
+       return NB_OK;
+}
+
 /*
  * XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family
  */
index 9cf4bb3e874a1a70ddf1ad3f21940e3fc57edf48..e5324dd8733f1d7a01f59ff40a37be2960154889 100644 (file)
@@ -182,6 +182,7 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty)
        }
 
        writes += pim_rp_config_write(pim, vty);
+       writes += pim_cand_config_write(pim, vty);
 
        if (pim->vrf->vrf_id == VRF_DEFAULT) {
                if (router->register_suppress_time
index e25eafc28e7859abfd17a02cbe7d0b199d77c433..ce4d85a2c8487bbb0a994cf36c8c2404ccdd9f05 100644 (file)
@@ -157,6 +157,8 @@ static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS)
                                pim_if_addr_add_all(ifp);
                }
        }
+
+       pim_cand_addrs_changed();
        return 0;
 }
 
@@ -205,6 +207,8 @@ static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS)
        }
 
        connected_free(&c);
+
+       pim_cand_addrs_changed();
        return 0;
 }
 
index 1e787a3525c63ee82b018ac59bf81c00024dd084..cf7818f4645a274c843c33f60ca9e8fb498c44ff 100644 (file)
@@ -76,6 +76,7 @@ pimd_pimd_SOURCES = \
 nodist_pimd_pimd_SOURCES = \
        yang/frr-pim.yang.c \
        yang/frr-pim-rp.yang.c \
+       yang/frr-pim-candidate.yang.c \
        yang/frr-gmp.yang.c \
        # end
 
@@ -89,6 +90,7 @@ pimd_pim6d_SOURCES = \
 nodist_pimd_pim6d_SOURCES = \
        yang/frr-pim.yang.c \
        yang/frr-pim-rp.yang.c \
+       yang/frr-pim-candidate.yang.c \
        yang/frr-gmp.yang.c \
        # end
 
diff --git a/yang/frr-pim-candidate.yang b/yang/frr-pim-candidate.yang
new file mode 100644 (file)
index 0000000..035343e
--- /dev/null
@@ -0,0 +1,136 @@
+module frr-pim-candidate {
+  yang-version "1.1";
+  namespace "http://frrouting.org/yang/pim-candidate";
+
+  prefix frr-pim-candidate;
+
+  import frr-interface {
+    prefix frr-interface;
+  }
+
+  import ietf-inet-types {
+    prefix "inet";
+  }
+
+  import frr-routing {
+    prefix "frr-rt";
+  }
+
+  import frr-pim {
+    prefix "frr-pim";
+  }
+
+  import frr-route-types {
+    prefix frr-route-types;
+  }
+
+  organization
+    "FRRouting";
+
+  contact
+    "FRR Users List:       <mailto:frog@lists.frrouting.org>
+     FRR Development List: <mailto:dev@lists.frrouting.org>";
+
+  description
+    "The module defines a collection of YANG definitions common for
+     all PIM (Protocol Independent Multicast) Candidate RP & BSR
+     (Rendezvous Point & Bootstrap Router) operation.
+
+     Copyright 2020 FRRouting
+
+     Redistribution and use in source and binary forms, with or without
+     modification, are permitted provided that the following conditions
+     are met:
+
+     1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+     2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+
+     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+     \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+     A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+     LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.";
+
+  revision 2021-05-04 {
+    description
+      "Initial revision.";
+    reference
+      "TBD";
+  }
+
+  /*
+   * Groupings
+   */
+  grouping candidate-rp-container {
+    description
+      "Grouping of Candidate RP settings.";
+
+    container candidate-rp {
+      presence
+        "Enable router to be a Candidate RP.";
+
+      description
+        "Candidate RP settings";
+
+      leaf rp-priority {
+        type uint8;
+        default "192";
+        description
+          "RP priority for this router, lower values win.";
+      }
+
+      leaf advertisement-interval {
+        type uint32 {
+          range 1..4294967295;
+        }
+        default "60";
+        description
+          "RP advertisement interval (seconds).  Holdtime is 2.5 times this.";
+      }
+
+      leaf-list group-list {
+        type frr-route-types:ip-multicast-group-prefix;
+        description
+          "List of multicast group address.";
+      }
+
+      choice source-address-or-interface {
+        description "IP address to use for RP operation";
+        default if-loopback;
+        leaf address {
+          type inet:ip-address;
+        }
+        leaf interface {
+          type frr-interface:interface-ref;
+        }
+        leaf if-loopback {
+          type empty;
+        }
+        leaf if-any {
+          type empty;
+        }
+      }
+    }
+  }
+
+  /*
+   * Configuration data nodes
+   */
+  augment "/frr-rt:routing/frr-rt:control-plane-protocols/"
+    + "frr-rt:control-plane-protocol/frr-pim:pim/"
+    + "frr-pim:address-family" {
+    description "PIM Candidate RP augmentation.";
+
+    uses candidate-rp-container;
+  }
+}
index 71aa04087858b699544b62991a2b5f75a7669235..786bd0bca65466712ad1109bfc11fde31257da35 100644 (file)
@@ -80,6 +80,7 @@ if PIMD
 dist_yangmodels_DATA += yang/frr-gmp.yang
 dist_yangmodels_DATA += yang/frr-pim.yang
 dist_yangmodels_DATA += yang/frr-pim-rp.yang
+dist_yangmodels_DATA += yang/frr-pim-candidate.yang
 endif
 
 if BGPD