summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJafar Al-Gharaibeh <jafar@atcorp.com>2024-07-23 07:45:02 +0200
committerJafar Al-Gharaibeh <jafar@atcorp.com>2024-09-09 19:42:28 +0200
commita110bb77989f9cd689a63d263cb22f69ef98f2a3 (patch)
treef579766eed7e0aca32471558f789aa6245cc6fb4
parentMerge pull request #16764 from LabNConsulting/chopps/fix-clang-sa-warning (diff)
downloadfrr-a110bb77989f9cd689a63d263cb22f69ef98f2a3.tar.xz
frr-a110bb77989f9cd689a63d263cb22f69ef98f2a3.zip
pimd: Candidate-RP support
Signed-off-by: David Lamparter <equinox@opensourcerouting.org> Signed-off-by: Jafar Al-Gharaibeh <jafar@atcorp.com>
-rw-r--r--pimd/pim6_cmd.c93
-rw-r--r--pimd/pim6_main.c1
-rw-r--r--pimd/pim_bsm.c429
-rw-r--r--pimd/pim_bsm.h67
-rw-r--r--pimd/pim_cmd.c99
-rw-r--r--pimd/pim_cmd.h1
-rw-r--r--pimd/pim_cmd_common.c49
-rw-r--r--pimd/pim_cmd_common.h7
-rw-r--r--pimd/pim_iface.c3
-rw-r--r--pimd/pim_main.c1
-rw-r--r--pimd/pim_nb.c64
-rw-r--r--pimd/pim_nb.h23
-rw-r--r--pimd/pim_nb_config.c239
-rw-r--r--pimd/pim_vty.c1
-rw-r--r--pimd/pim_zebra.c4
-rw-r--r--pimd/subdir.am2
-rw-r--r--yang/frr-pim-candidate.yang136
-rw-r--r--yang/subdir.am1
18 files changed, 1206 insertions, 14 deletions
diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c
index f7a4e0e48..838a6d174 100644
--- a/pimd/pim6_cmd.c
+++ b/pimd/pim6_cmd.c
@@ -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);
diff --git a/pimd/pim6_main.c b/pimd/pim6_main.c
index 24443404e..07b70ae2b 100644
--- a/pimd/pim6_main.c
+++ b/pimd/pim6_main.c
@@ -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,
};
diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c
index 2d451718a..c3c444480 100644
--- a/pimd/pim_bsm.c
+++ b/pimd/pim_bsm.c
@@ -23,6 +23,12 @@
#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 */
diff --git a/pimd/pim_bsm.h b/pimd/pim_bsm.h
index fb09e3b1c..27714ab45 100644
--- a/pimd/pim_bsm.h
+++ b/pimd/pim_bsm.h
@@ -21,6 +21,10 @@
#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
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 633c46966..88c9a4b22 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -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);
diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h
index d39d77cd2..89ca77dca 100644
--- a/pimd/pim_cmd.h
+++ b/pimd/pim_cmd.h
@@ -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"
diff --git a/pimd/pim_cmd_common.c b/pimd/pim_cmd_common.c
index a87f5b698..0ab2b841d 100644
--- a/pimd/pim_cmd_common.c
+++ b/pimd/pim_cmd_common.c
@@ -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)
{
diff --git a/pimd/pim_cmd_common.h b/pimd/pim_cmd_common.h
index da2e44be5..6ba126ef8 100644
--- a/pimd/pim_cmd_common.h
+++ b/pimd/pim_cmd_common.h
@@ -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);
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index 45a2435ae..125d35ac4 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -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;
}
diff --git a/pimd/pim_main.c b/pimd/pim_main.c
index 8f2ce0bed..f88aca719 100644
--- a/pimd/pim_main.c
+++ b/pimd/pim_main.c
@@ -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,
};
diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c
index c154c18af..53e7147d5 100644
--- a/pimd/pim_nb.c
+++ b/pimd/pim_nb.c
@@ -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",
diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h
index fc4c11cea..024c4b6c9 100644
--- a/pimd/pim_nb.h
+++ b/pimd/pim_nb.h
@@ -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 \
diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c
index 037bfea78..0faddef50 100644
--- a/pimd/pim_nb_config.c
+++ b/pimd/pim_nb_config.c
@@ -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
*/
diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c
index 9cf4bb3e8..e5324dd87 100644
--- a/pimd/pim_vty.c
+++ b/pimd/pim_vty.c
@@ -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
diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c
index e25eafc28..ce4d85a2c 100644
--- a/pimd/pim_zebra.c
+++ b/pimd/pim_zebra.c
@@ -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;
}
diff --git a/pimd/subdir.am b/pimd/subdir.am
index 1e787a352..cf7818f46 100644
--- a/pimd/subdir.am
+++ b/pimd/subdir.am
@@ -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
index 000000000..035343edd
--- /dev/null
+++ b/yang/frr-pim-candidate.yang
@@ -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;
+ }
+}
diff --git a/yang/subdir.am b/yang/subdir.am
index 71aa04087..786bd0bca 100644
--- a/yang/subdir.am
+++ b/yang/subdir.am
@@ -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