pimd: Candidate-BSR support

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Signed-off-by: Jafar Al-Gharaibeh <jafar@atcorp.com>
This commit is contained in:
Jafar Al-Gharaibeh 2024-07-26 09:57:44 -05:00
parent a110bb7798
commit 2d0812373c
19 changed files with 1651 additions and 87 deletions

View file

@ -1259,6 +1259,27 @@ DEFPY (no_ipv6_pim_ucast_bsm,
return pim_process_no_unicast_bsm_cmd(vty);
}
DEFPY (pim6_bsr_candidate_bsr,
pim6_bsr_candidate_bsr_cmd,
"[no] bsr candidate-bsr [{priority (0-255)|source <address X:X::X:X|interface IFNAME|loopback$loopback|any$any>}]",
NO_STR
BSR_STR
"Make this router a Candidate BSR\n"
"BSR Priority (higher wins)\n"
"BSR Priority (higher wins)\n"
"Specify IP address for BSR 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_BSR_XPATH, no,
false, any, ifname, address_str,
priority_str, NULL);
}
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>}]",
@ -1809,6 +1830,51 @@ DEFPY (show_ipv6_pim_cand_rp,
return CMD_SUCCESS;
}
DEFPY (show_ipv6_pim_bsr_rpdb,
show_ipv6_pim_bsr_rpdb_cmd,
"show ipv6 pim bsr candidate-rps [vrf VRF_NAME] [json$uj]",
SHOW_STR
IPV6_STR
PIM_STR
"boot-strap router information\n"
"Candidate RPs\n"
VRF_CMD_HELP_STR
JSON_STR)
{
struct vrf *vrf = pim_cmd_lookup(vty, vrf_name);
if (!vrf || !vrf->info)
return CMD_WARNING;
struct pim_instance *pim = vrf->info;
struct bsm_scope *scope = &pim->global_scope;
return pim_crp_db_show(vty, scope);
}
DEFPY (show_ipv6_pim_bsr_groups,
show_ipv6_pim_bsr_groups_cmd,
"show ipv6 pim bsr groups [vrf VRF_NAME] [json$uj]",
SHOW_STR
IPV6_STR
PIM_STR
"boot-strap router information\n"
"Candidate RP groups\n"
VRF_CMD_HELP_STR
JSON_STR)
{
struct vrf *vrf = pim_cmd_lookup(vty, vrf_name);
if (!vrf || !vrf->info)
return CMD_WARNING;
struct pim_instance *pim = vrf->info;
struct bsm_scope *scope = &pim->global_scope;
return pim_crp_groups_show(vty, scope);
}
DEFPY (show_ipv6_pim_statistics,
show_ipv6_pim_statistics_cmd,
"show ipv6 pim [vrf NAME] statistics [interface WORD$word] [json$json]",
@ -2742,6 +2808,7 @@ void pim_cmd_init(void)
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(PIM6_NODE, &pim6_bsr_candidate_bsr_cmd);
install_element(CONFIG_NODE, &ipv6_mld_group_watermark_cmd);
install_element(VRF_NODE, &ipv6_mld_group_watermark_cmd);
@ -2798,6 +2865,8 @@ void pim_cmd_init(void)
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_bsr_rpdb_cmd);
install_element(VIEW_NODE, &show_ipv6_pim_bsr_groups_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);

View file

@ -10,6 +10,17 @@
#include "config.h"
#endif
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <lib/network.h>
#include <lib/iana_afi.h>
#include <lib/sockunion.h>
#include <lib/sockopt.h>
#include "if.h"
#include "pimd.h"
#include "pim_iface.h"
@ -25,21 +36,19 @@
#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);
static void pim_g2rp_timer_start(struct bsm_rpinfo *bsrp, int hold_time);
static inline void pim_g2rp_timer_restart(struct bsm_rpinfo *bsrp,
int hold_time);
static void pim_bsm_accept_any(struct bsm_scope *scope);
static void pim_cand_bsr_trigger(struct bsm_scope *scope, bool verbose);
static void pim_cand_bsr_pending(struct bsm_scope *scope);
/* Memory Types */
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(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");
@ -106,7 +115,7 @@ static void pim_bsm_frag_free(struct bsm_frag *bsfrag)
XFREE(MTYPE_PIM_BSM_FRAG, bsfrag);
}
static void pim_bsm_frags_free(struct bsm_scope *scope)
void pim_bsm_frags_free(struct bsm_scope *scope)
{
struct bsm_frag *bsfrag;
@ -156,12 +165,12 @@ static struct bsgrp_node *pim_bsm_new_bsgrp_node(struct route_table *rt,
return bsgrp;
}
/* BS timer for NO_INFO, ACCEPT_ANY & ACCEPT_PREFERRED.
* Candidate BSR handling is separate further below
*/
static void pim_on_bs_timer(struct event *t)
{
struct route_node *rn;
struct bsm_scope *scope;
struct bsgrp_node *bsgrp_node;
struct bsm_rpinfo *bsrp;
scope = EVENT_ARG(t);
EVENT_OFF(scope->bs_timer);
@ -170,7 +179,20 @@ static void pim_on_bs_timer(struct event *t)
zlog_debug("%s: Bootstrap Timer expired for scope: %d",
__func__, scope->sz_id);
assertf(scope->state <= ACCEPT_PREFERRED, "state=%d", scope->state);
pim_nht_bsr_del(scope->pim, scope->current_bsr);
pim_bsm_accept_any(scope);
}
static void pim_bsm_accept_any(struct bsm_scope *scope)
{
struct route_node *rn;
struct bsgrp_node *bsgrp_node;
struct bsm_rpinfo *bsrp;
EVENT_OFF(scope->t_ebsr_regen_bsm);
/* Reset scope zone data */
scope->state = ACCEPT_ANY;
scope->current_bsr = PIMADDR_ANY;
@ -197,6 +219,11 @@ static void pim_on_bs_timer(struct event *t)
pim_bsm_rpinfos_free(bsgrp_node->partial_bsrp_list);
bsgrp_node->pend_rp_cnt = 0;
}
/* we're leaving ACCEPT_PREFERRED, which doubles as C-BSR if we're
* configured to be a Candidate BSR. See if we're P-BSR now.
*/
pim_cand_bsr_trigger(scope, false);
}
static void pim_bs_timer_stop(struct bsm_scope *scope)
@ -228,6 +255,71 @@ static inline void pim_bs_timer_restart(struct bsm_scope *scope, int bs_timeout)
pim_bs_timer_start(scope, bs_timeout);
}
static void bsm_unicast_sock_read(struct event *t)
{
struct bsm_scope *scope = EVENT_ARG(t);
struct sockaddr_storage from;
struct sockaddr_storage to;
socklen_t fromlen = sizeof(from);
socklen_t tolen = sizeof(to);
ifindex_t ifindex = 0;
struct interface *ifp;
uint8_t buf[PIM_PIM_BUFSIZE_READ];
int len, i;
event_add_read(router->master, bsm_unicast_sock_read, scope,
scope->unicast_sock, &scope->unicast_read);
for (i = 0; i < router->packet_process; i++) {
pim_sgaddr sg;
len = pim_socket_recvfromto(scope->unicast_sock, buf,
sizeof(buf), &from, &fromlen, &to,
&tolen, &ifindex);
if (len < 0) {
if (errno == EINTR)
continue;
if (errno == EWOULDBLOCK || errno == EAGAIN)
break;
if (PIM_DEBUG_PIM_PACKETS)
zlog_debug("Received errno: %d %s", errno,
safe_strerror(errno));
break;
}
#if PIM_IPV == 4
sg.src = ((struct sockaddr_in *)&from)->sin_addr;
sg.grp = ((struct sockaddr_in *)&to)->sin_addr;
#else
sg.src = ((struct sockaddr_in6 *)&from)->sin6_addr;
sg.grp = ((struct sockaddr_in6 *)&to)->sin6_addr;
#endif
/*
* What? So with vrf's the incoming packet is received
* on the vrf interface but recvfromto above returns
* the right ifindex, so just use it. We know
* it's the right interface because we bind to it
*/
ifp = if_lookup_by_index(ifindex, scope->pim->vrf->vrf_id);
if (!ifp) {
zlog_warn("Received incoming PIM packet on unknown ifindex %d",
ifindex);
break;
}
int fail = pim_pim_packet(ifp, buf, len, sg, false);
if (fail) {
if (PIM_DEBUG_PIM_PACKETS)
zlog_debug("%s: pim_pim_packet() return=%d",
__func__, fail);
break;
}
}
}
void pim_bsm_proc_init(struct pim_instance *pim)
{
struct bsm_scope *scope = &pim->global_scope;
@ -248,12 +340,19 @@ void pim_bsm_proc_init(struct pim_instance *pim)
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);
if (setsockopt_ifindex(PIM_AF, scope->unicast_sock, 1) == -1)
zlog_warn("%s: Without IP_PKTINFO, src interface can't be determined",
__func__);
pim_socket_ip_hdr(scope->unicast_sock);
frr_with_privs (&pimd_privs) {
vrf_bind(pim->vrf->vrf_id, scope->unicast_sock, NULL);
}
event_add_read(router->master, bsm_unicast_sock_read, scope,
scope->unicast_sock, &scope->unicast_read);
}
void pim_bsm_proc_free(struct pim_instance *pim)
@ -263,6 +362,7 @@ void pim_bsm_proc_free(struct pim_instance *pim)
struct bsgrp_node *bsgrp;
struct cand_rp_group *crpgrp;
EVENT_OFF(scope->unicast_read);
close(scope->unicast_sock);
pim_bs_timer_stop(scope);
@ -551,9 +651,6 @@ static void pim_instate_pend_list(struct bsgrp_node *bsgrp_node)
static bool is_preferred_bsr(struct pim_instance *pim, pim_addr bsr,
uint32_t bsr_prio)
{
if (!pim_addr_cmp(bsr, pim->global_scope.current_bsr))
return true;
if (bsr_prio > pim->global_scope.current_bsr_prio)
return true;
@ -562,6 +659,11 @@ static bool is_preferred_bsr(struct pim_instance *pim, pim_addr bsr,
return true;
else
return false;
} else if (!pim_addr_cmp(bsr, pim->global_scope.current_bsr)) {
/* BSR config changed, lower prio now. local BSR check
* is handled separately in pim_bsm_update()
*/
return true;
} else
return false;
}
@ -569,20 +671,53 @@ static bool is_preferred_bsr(struct pim_instance *pim, pim_addr bsr,
static void pim_bsm_update(struct pim_instance *pim, pim_addr bsr,
uint32_t bsr_prio)
{
if (pim_addr_cmp(bsr, pim->global_scope.current_bsr)) {
pim->global_scope.current_bsr_prio = bsr_prio;
pim->global_scope.current_bsr_last_ts = pim_time_monotonic_sec();
if (pim->global_scope.bsr_addrsel.run &&
pim->global_scope.cand_bsr_prio > bsr_prio &&
pim->global_scope.state < BSR_PENDING) {
/* current BSR is now less preferred than ourselves */
pim_cand_bsr_pending(&pim->global_scope);
return;
}
if (!pim_addr_cmp(bsr, pim->global_scope.current_bsr))
return;
switch (pim->global_scope.state) {
case BSR_PENDING:
if (PIM_DEBUG_BSM)
zlog_debug("Candidate BSR dropping out of BSR election, better BSR (%u, %pPA)",
bsr_prio, &bsr);
break;
case BSR_ELECTED:
if (PIM_DEBUG_BSM)
zlog_debug("Lost BSR status, better BSR (%u, %pPA)",
bsr_prio, &bsr);
break;
case NO_INFO:
case ACCEPT_ANY:
case ACCEPT_PREFERRED:
break;
}
EVENT_OFF(pim->global_scope.t_ebsr_regen_bsm);
if (pim->global_scope.state == BSR_ELECTED)
pim_crp_db_clear(&pim->global_scope);
else
pim_nht_bsr_del(pim, pim->global_scope.current_bsr);
pim_nht_bsr_add(pim, bsr);
pim->global_scope.current_bsr = bsr;
pim->global_scope.current_bsr_first_ts =
pim_time_monotonic_sec();
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();
}
void pim_bsm_clear(struct pim_instance *pim)
{
@ -596,6 +731,11 @@ void pim_bsm_clear(struct pim_instance *pim)
struct rp_info *rp_info;
bool upstream_updated = false;
EVENT_OFF(pim->global_scope.t_ebsr_regen_bsm);
if (pim->global_scope.state == BSR_ELECTED)
pim_crp_db_clear(&pim->global_scope);
else
pim_nht_bsr_del(pim, pim->global_scope.current_bsr);
/* Reset scope zone data */
@ -1157,7 +1297,7 @@ static void pim_update_pending_rp_cnt(struct bsm_scope *sz,
}
/* Parsing BSR packet and adding to partial list of corresponding bsgrp node */
static bool pim_bsm_parse_install_g2rp(struct bsm_scope *scope, uint8_t *buf,
bool pim_bsm_parse_install_g2rp(struct bsm_scope *scope, uint8_t *buf,
int buflen, uint16_t bsm_frag_tag)
{
struct bsmmsg_grpinfo grpinfo;
@ -1379,35 +1519,6 @@ int pim_bsm_process(struct interface *ifp, pim_sgaddr *sg, uint8_t *buf,
}
}
/* Drop if bsr is not preferred bsr */
if (!is_preferred_bsr(pim, bsr_addr, bshdr->bsr_prio)) {
if (PIM_DEBUG_BSM)
zlog_debug("%s : Received a non-preferred BSM",
__func__);
pim->bsm_dropped++;
return -1;
}
if (no_fwd) {
/* only accept no-forward BSM if quick refresh on startup */
if ((pim->global_scope.accept_nofwd_bsm)
|| (frag_tag == pim->global_scope.bsm_frag_tag)) {
pim->global_scope.accept_nofwd_bsm = false;
} else {
if (PIM_DEBUG_BSM)
zlog_debug(
"%s : nofwd_bsm received on %pPAs when accpt_nofwd_bsm false",
__func__, &bsr_addr);
pim->bsm_dropped++;
pim_ifp->pim_ifstat_ucast_bsm_cfg_miss++;
return -1;
}
}
/* BSM packet is seen, so resetting accept_nofwd_bsm to false */
if (pim->global_scope.accept_nofwd_bsm)
pim->global_scope.accept_nofwd_bsm = false;
if (!pim_addr_cmp(sg->grp, qpim_all_pim_routers_addr)) {
/* Multicast BSMs are only accepted if source interface & IP
* match RPF towards the BSR's IP address, or they have
@ -1444,6 +1555,57 @@ int pim_bsm_process(struct interface *ifp, pim_sgaddr *sg, uint8_t *buf,
return -1;
}
/* when the BSR restarts, it can get its own BSR advertisement thrown
* back at it, and without this we'll go into ACCEPT_PREFERRED with
* ourselves as the BSR when we should be in BSR_ELECTED.
*/
if (if_address_is_local(&bshdr->bsr_addr.addr, PIM_AF,
pim->vrf->vrf_id)) {
if (PIM_DEBUG_BSM)
zlog_debug("%s : Dropping BSM from ourselves", __func__);
pim->bsm_dropped++;
return -1;
}
/* Drop if bsr is not preferred bsr */
if (!is_preferred_bsr(pim, bsr_addr, bshdr->bsr_prio)) {
if (pim->global_scope.state == BSR_PENDING && !no_fwd) {
/* in P-BSR state, non-preferred BSMs are forwarded, but
* content is ignored.
*/
if (PIM_DEBUG_BSM)
zlog_debug("%s : Forwarding non-preferred BSM during Pending-BSR state",
__func__);
pim_bsm_fwd_whole_sz(pim_ifp->pim, buf, buf_size, sz);
return -1;
}
if (PIM_DEBUG_BSM)
zlog_debug("%s : Received a non-preferred BSM",
__func__);
pim->bsm_dropped++;
return -1;
}
if (no_fwd) {
/* only accept no-forward BSM if quick refresh on startup */
if ((pim->global_scope.accept_nofwd_bsm) ||
(frag_tag == pim->global_scope.bsm_frag_tag)) {
pim->global_scope.accept_nofwd_bsm = false;
} else {
if (PIM_DEBUG_BSM)
zlog_debug("%s : nofwd_bsm received on %pPAs when accpt_nofwd_bsm false",
__func__, &bsr_addr);
pim->bsm_dropped++;
pim_ifp->pim_ifstat_ucast_bsm_cfg_miss++;
return -1;
}
}
/* BSM packet is seen, so resetting accept_nofwd_bsm to false */
if (pim->global_scope.accept_nofwd_bsm)
pim->global_scope.accept_nofwd_bsm = false;
if (empty_bsm) {
if (PIM_DEBUG_BSM)
zlog_debug("%s : Empty Pref BSM received", __func__);
@ -1454,9 +1616,8 @@ int pim_bsm_process(struct interface *ifp, pim_sgaddr *sg, uint8_t *buf,
(buf + PIM_BSM_HDR_LEN + PIM_MSG_HEADER_LEN),
(buf_size - PIM_BSM_HDR_LEN - PIM_MSG_HEADER_LEN),
frag_tag)) {
if (PIM_DEBUG_BSM) {
zlog_debug("%s, Parsing BSM failed.", __func__);
}
zlog_warn("BSM from %pPA failed to parse",
(pim_addr *)&bshdr->bsr_addr.addr);
pim->bsm_dropped++;
return -1;
}
@ -1493,6 +1654,148 @@ int pim_bsm_process(struct interface *ifp, pim_sgaddr *sg, uint8_t *buf,
return 0;
}
static void pim_elec_bsr_timer(struct event *t)
{
struct bsm_scope *scope = EVENT_ARG(t);
struct bsm_frag *frag;
struct bsm_hdr *hdr;
assert(scope->state == BSR_ELECTED);
scope->bsm_frag_tag++;
frag = bsm_frags_first(scope->bsm_frags);
assert(frag);
hdr = (struct bsm_hdr *)(frag->data + PIM_MSG_HEADER_LEN);
hdr->frag_tag = htons(scope->bsm_frag_tag);
unsigned int timer = PIM_BS_TIME;
if (scope->changed_bsm_trigger) {
if (PIM_DEBUG_BSM)
zlog_debug("Sending triggered BSM");
scope->changed_bsm_trigger--;
timer = 5;
} else {
if (PIM_DEBUG_BSM)
zlog_debug("Sending scheduled BSM");
pim_bsm_sent(scope);
}
pim_bsm_fwd_whole_sz(scope->pim, frag->data, frag->size, scope->sz_id);
scope->current_bsr_last_ts = pim_time_monotonic_sec();
event_add_timer(router->master, pim_elec_bsr_timer, scope, timer,
&scope->bs_timer);
}
void pim_bsm_changed(struct bsm_scope *scope)
{
struct event t;
EVENT_OFF(scope->bs_timer);
scope->changed_bsm_trigger = 2;
t.arg = scope;
pim_elec_bsr_timer(&t);
}
static void pim_cand_bsr_pending_expire(struct event *t)
{
struct bsm_scope *scope = EVENT_ARG(t);
assertf(scope->state == BSR_PENDING, "state=%d", scope->state);
assertf(pim_addr_is_any(scope->current_bsr), "current_bsr=%pPA",
&scope->current_bsr);
if (PIM_DEBUG_BSM)
zlog_debug("Elected BSR, wait expired without preferable BSMs");
scope->state = BSR_ELECTED;
scope->current_bsr_prio = scope->cand_bsr_prio;
scope->current_bsr = scope->bsr_addrsel.run_addr;
scope->bsm_frag_tag = frr_weak_random();
scope->current_bsr_first_ts = pim_time_monotonic_sec();
pim_cand_rp_trigger(scope);
pim_bsm_generate(scope);
}
#if PIM_IPV == 6
static float bsr_addr_delay(pim_addr best, pim_addr local)
{
unsigned int pos;
uint32_t best_4b, local_4b;
float delay_log;
for (pos = 0; pos < 12; pos++) {
if (best.s6_addr[pos] != local.s6_addr[pos])
break;
}
memcpy(&best_4b, &best.s6_addr[pos], 4);
memcpy(&local_4b, &local.s6_addr[pos], 4);
delay_log = log2(1 + ntohl(best_4b) - ntohl(local_4b));
delay_log += (12 - pos) * 8;
return delay_log / 64.;
}
#endif
static void pim_cand_bsr_pending(struct bsm_scope *scope)
{
unsigned int bs_rand_override;
uint8_t best_prio;
pim_addr best_addr;
float prio_delay, addr_delay;
EVENT_OFF(scope->bs_timer);
EVENT_OFF(scope->t_ebsr_regen_bsm);
scope->state = BSR_PENDING;
best_prio = MAX(scope->cand_bsr_prio, scope->current_bsr_prio);
best_addr = pim_addr_cmp(scope->bsr_addrsel.run_addr,
scope->current_bsr) > 0
? scope->bsr_addrsel.run_addr
: scope->current_bsr;
/* RFC5059 sec.5 */
#if PIM_IPV == 4
if (scope->cand_bsr_prio == best_prio) {
prio_delay = 0.; /* log2(1) = 0 */
addr_delay = log2(1 + ntohl(best_addr.s_addr) -
ntohl(scope->bsr_addrsel.run_addr.s_addr)) /
16.;
} else {
prio_delay = 2. * log2(1 + best_prio - scope->cand_bsr_prio);
addr_delay = 2 - (ntohl(scope->bsr_addrsel.run_addr.s_addr) /
(float)(1 << 31));
}
#else
if (scope->cand_bsr_prio == best_prio) {
prio_delay = 0.; /* log2(1) = 0 */
addr_delay = bsr_addr_delay(best_addr,
scope->bsr_addrsel.run_addr);
} else {
prio_delay = 2. * log2(1 + best_prio - scope->cand_bsr_prio);
addr_delay = 2 -
(ntohl(scope->bsr_addrsel.run_addr.s6_addr32[0]) /
(float)(1 << 31));
}
#endif
bs_rand_override = 5000 + (int)((prio_delay + addr_delay) * 1000.);
if (PIM_DEBUG_BSM)
zlog_debug("Pending-BSR (%u, %pPA), waiting %ums",
scope->cand_bsr_prio, &scope->bsr_addrsel.run_addr,
bs_rand_override);
event_add_timer_msec(router->master, pim_cand_bsr_pending_expire, scope,
bs_rand_override, &scope->bs_timer);
}
static inline pim_addr if_highest_addr(pim_addr cur, struct interface *ifp)
{
struct connected *connected;
@ -1575,6 +1878,84 @@ out_disable:
return prev_run;
}
static void pim_cand_bsr_stop(struct bsm_scope *scope, bool verbose)
{
cand_addrsel_clear(&scope->bsr_addrsel);
switch (scope->state) {
case NO_INFO:
case ACCEPT_ANY:
case ACCEPT_PREFERRED:
return;
case BSR_PENDING:
case BSR_ELECTED:
break;
}
if (PIM_DEBUG_BSM)
zlog_debug("Candidate BSR ceasing operation");
EVENT_OFF(scope->t_ebsr_regen_bsm);
EVENT_OFF(scope->bs_timer);
pim_crp_db_clear(scope);
pim_bsm_accept_any(scope);
}
static void pim_cand_bsr_trigger(struct bsm_scope *scope, bool verbose)
{
/* this is called on all state changes even if we aren't configured
* to be C-BSR at all.
*/
if (!scope->bsr_addrsel.run)
return;
if (scope->current_bsr_prio > scope->cand_bsr_prio) {
assert(scope->state == ACCEPT_PREFERRED);
if (!verbose)
return;
if (PIM_DEBUG_BSM)
zlog_debug("Candidate BSR: known better BSR %pPA (higher priority %u > %u)",
&scope->current_bsr, scope->current_bsr_prio,
scope->cand_bsr_prio);
return;
} else if (scope->current_bsr_prio == scope->cand_bsr_prio &&
pim_addr_cmp(scope->current_bsr,
scope->bsr_addrsel.run_addr) > 0) {
assert(scope->state == ACCEPT_PREFERRED);
if (!verbose)
return;
if (PIM_DEBUG_BSM)
zlog_debug("Candidate BSR: known better BSR %pPA (higher address > %pPA)",
&scope->current_bsr,
&scope->bsr_addrsel.run_addr);
return;
}
if (!pim_addr_cmp(scope->current_bsr, scope->bsr_addrsel.run_addr))
return;
pim_cand_bsr_pending(scope);
}
void pim_cand_bsr_apply(struct bsm_scope *scope)
{
if (!cand_addrsel_update(&scope->bsr_addrsel, scope->pim->vrf))
return;
if (!scope->bsr_addrsel.run) {
pim_cand_bsr_stop(scope, true);
return;
}
if (PIM_DEBUG_BSM)
zlog_debug("Candidate BSR: %pPA, priority %u",
&scope->bsr_addrsel.run_addr, scope->cand_bsr_prio);
pim_cand_bsr_trigger(scope, true);
}
static void pim_cand_rp_adv_stop_maybe(struct bsm_scope *scope)
{
/* actual check whether stop should be sent - covers address
@ -1587,11 +1968,12 @@ static void pim_cand_rp_adv_stop_maybe(struct bsm_scope *scope)
switch (scope->state) {
case ACCEPT_PREFERRED:
/* TBD: BSR_ELECTED */
case BSR_ELECTED:
break;
case NO_INFO:
case ACCEPT_ANY:
case BSR_PENDING:
default:
return;
}
@ -1636,10 +2018,11 @@ static void pim_cand_rp_adv(struct event *t)
switch (scope->state) {
case ACCEPT_PREFERRED:
/* TBD: BSR_ELECTED */
case BSR_ELECTED:
break;
case ACCEPT_ANY:
case BSR_PENDING:
case NO_INFO:
default:
/* state change will retrigger */
@ -1797,7 +2180,8 @@ static void pim_cand_addrs_reapply(struct event *t)
if (!pi)
continue;
/* this calls cand_addrsel_update() and applies changes */
/* these call cand_addrsel_update() and apply changes */
pim_cand_bsr_apply(&pi->global_scope);
pim_cand_rp_apply(&pi->global_scope);
}
}
@ -1850,10 +2234,14 @@ int pim_cand_config_write(struct pim_instance *pim, struct vty *vty)
ret++;
}
}
if (scope->bsr_addrsel.cfg_enable) {
vty_out(vty, " bsr candidate-bsr");
if (scope->cand_bsr_prio != 64)
vty_out(vty, " priority %u", scope->cand_bsr_prio);
cand_addrsel_config_write(vty, &scope->bsr_addrsel);
vty_out(vty, "\n");
ret++;
}
return ret;
}
void pim_crp_nht_update(struct pim_instance *pim, struct pim_nexthop_cache *pnc)
{
/* stub for Candidate-RP */
}

View file

@ -21,6 +21,9 @@
#define PIM_BS_TIME 60 /* RFC 5059 - Sec 5 */
#define PIM_BSR_DEFAULT_TIMEOUT 130 /* RFC 5059 - Sec 5 */
/* number of times to include rp-count = 0 ranges */
#define PIM_BSR_DEAD_COUNT 3
#define PIM_CRP_ADV_TRIGCOUNT 3
#define PIM_CRP_ADV_INTERVAL 60
#define PIM_CRP_HOLDTIME 150
@ -37,11 +40,21 @@
* ==============
*/
/* Non candidate BSR states */
enum ncbsr_state {
/* BSR states
*
* Candidate BSR starts at BSR_PENDING, moves to AP or E depending on
* loss/win. Will never go into AA (because in that case it'd become BSR
* itself.)
*
* Non-Candidate BSR starts at NO_INFO, moves to AP & AA depending on
* a BSR being available or not.
*/
enum bsr_state {
NO_INFO = 0,
ACCEPT_ANY,
ACCEPT_PREFERRED
ACCEPT_PREFERRED, /* = same as C-BSR if candidate */
BSR_PENDING,
BSR_ELECTED,
};
enum cand_addr {
@ -51,7 +64,7 @@ enum cand_addr {
CAND_ADDR_EXPLICIT,
};
/* used separately for Cand-RP, and (TBD) Cand-BSR */
/* used separately for Cand-RP and Cand-BSR */
struct cand_addrsel {
bool cfg_enable;
enum cand_addr cfg_mode : 8;
@ -70,10 +83,18 @@ struct cand_addrsel {
PREDECL_DLIST(bsm_frags);
PREDECL_RBTREE_UNIQ(cand_rp_groups);
/* n*m "table" accessed both by-RP and by-group */
PREDECL_RBTREE_UNIQ(bsr_crp_rps);
PREDECL_RBTREE_UNIQ(bsr_crp_groups);
PREDECL_RBTREE_UNIQ(bsr_crp_rp_groups);
PREDECL_RBTREE_UNIQ(bsr_crp_group_rps);
/* BSM scope - bsm processing is per scope */
struct bsm_scope {
int sz_id; /* scope zone id */
enum ncbsr_state state; /* non candidate BSR state */
enum bsr_state state; /* BSR state */
bool accept_nofwd_bsm; /* no fwd bsm accepted for scope */
pim_addr current_bsr; /* current elected BSR for the sz */
uint32_t current_bsr_prio; /* current BSR priority */
@ -89,6 +110,31 @@ struct bsm_scope {
struct route_table *bsrp_table; /* group2rp mapping rcvd from BSR */
struct event *bs_timer; /* Boot strap timer */
/* Candidate BSR config */
struct cand_addrsel bsr_addrsel;
uint8_t cand_bsr_prio;
/* Candidate BSR state */
uint8_t current_cand_bsr_prio;
/* if nothing changed from Cand-RP data we received, less work... */
bool elec_rp_data_changed;
/* data that the E-BSR keeps - not to be confused with Candidate-RP
* stuff below. These two here are the info about all the Cand-RPs
* that we as a BSR received information for in Cand-RP-adv packets.
*/
struct bsr_crp_rps_head ebsr_rps[1];
struct bsr_crp_groups_head ebsr_groups[1];
/* set if we have any group ranges where we're currently advertising
* rp-count = 0 (includes both ranges without any RPs as well as
* ranges with only NHT-unreachable RPs)
*/
bool ebsr_have_dead_pending;
unsigned int changed_bsm_trigger;
struct event *t_ebsr_regen_bsm;
/* Candidate RP config */
struct cand_addrsel cand_rp_addrsel;
uint8_t cand_rp_prio;
@ -98,6 +144,7 @@ struct bsm_scope {
/* Candidate RP state */
int unicast_sock;
struct event *unicast_read;
struct event *cand_rp_adv_timer;
unsigned int cand_rp_adv_trigger; /* # trigg. C-RP-Adv left to send */
@ -111,6 +158,46 @@ struct cand_rp_group {
prefix_pim p;
};
struct bsr_crp_group {
struct bsr_crp_groups_item item;
prefix_pim range;
struct bsr_crp_group_rps_head rps[1];
size_t n_selected;
bool deleted_selected : 1;
/* number of times we've advertised this range with rp-count = 0 */
unsigned int dead_count;
};
struct bsr_crp_rp {
struct bsr_crp_rps_item item;
pim_addr addr;
struct bsr_crp_rp_groups_head groups[1];
struct bsm_scope *scope;
struct event *t_hold;
time_t seen_first;
time_t seen_last;
uint16_t holdtime;
uint8_t prio;
bool nht_ok;
};
/* "n * m" RP<->Group tie-in */
struct bsr_crp_item {
struct bsr_crp_rp_groups_item r_g_item;
struct bsr_crp_group_rps_item g_r_item;
struct bsr_crp_group *group;
struct bsr_crp_rp *rp;
bool selected : 1;
};
/* BSM packet (= fragment) - this is stored as list in bsm_frags inside scope
* This is used for forwarding to new neighbors or restarting mcast routers
*/
@ -268,6 +355,15 @@ 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_bsm_generate(struct bsm_scope *scope);
void pim_bsm_changed(struct bsm_scope *scope);
void pim_bsm_sent(struct bsm_scope *scope);
void pim_bsm_frags_free(struct bsm_scope *scope);
bool pim_bsm_parse_install_g2rp(struct bsm_scope *scope, uint8_t *buf,
int buflen, uint16_t bsm_frag_tag);
void pim_cand_bsr_apply(struct bsm_scope *scope);
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);
@ -275,6 +371,42 @@ void pim_cand_rp_grp_del(struct bsm_scope *scope, const prefix_pim *p);
void pim_cand_addrs_changed(void);
int pim_crp_process(struct interface *ifp, pim_sgaddr *src_dst, uint8_t *buf,
uint32_t buf_size);
struct pim_nexthop_cache;
void pim_crp_nht_update(struct pim_instance *pim, struct pim_nexthop_cache *pnc);
void pim_crp_db_clear(struct bsm_scope *scope);
int pim_crp_db_show(struct vty *vty, struct bsm_scope *scope);
int pim_crp_groups_show(struct vty *vty, struct bsm_scope *scope);
int pim_crp_process(struct interface *ifp, pim_sgaddr *src_dst, uint8_t *buf,
uint32_t buf_size);
struct pim_nexthop_cache;
void pim_crp_nht_update(struct pim_instance *pim, struct pim_nexthop_cache *pnc);
void pim_crp_db_clear(struct bsm_scope *scope);
int pim_crp_db_show(struct vty *vty, struct bsm_scope *scope);
int pim_crp_groups_show(struct vty *vty, struct bsm_scope *scope);
int pim_crp_process(struct interface *ifp, pim_sgaddr *src_dst, uint8_t *buf,
uint32_t buf_size);
struct pim_nexthop_cache;
void pim_crp_nht_update(struct pim_instance *pim, struct pim_nexthop_cache *pnc);
void pim_crp_db_clear(struct bsm_scope *scope);
int pim_crp_db_show(struct vty *vty, struct bsm_scope *scope);
int pim_crp_groups_show(struct vty *vty, struct bsm_scope *scope);
int pim_cand_config_write(struct pim_instance *pim, struct vty *vty);
DECLARE_MTYPE(PIM_BSM_FRAG);
DECLARE_MTYPE(PIM_BSM_FRAG);
DECLARE_MTYPE(PIM_BSM_FRAG);
#endif

634
pimd/pim_bsr_rpdb.c Normal file
View file

@ -0,0 +1,634 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* PIM RP database for BSR operation
* Copyright (C) 2021 David Lamparter for NetDEF, Inc.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <lib/network.h>
#include <lib/iana_afi.h>
#include <lib/sockunion.h>
#include "if.h"
#include "pimd.h"
#include "pim_iface.h"
#include "pim_instance.h"
#include "pim_rpf.h"
#include "pim_hello.h"
#include "pim_pim.h"
#include "pim_nht.h"
#include "pim_bsm.h"
#include "pim_time.h"
/* safety limits to prevent DoS/memory exhaustion attacks against the BSR
*
* The BSR is more susceptible than other PIM protocol operation because
* Candidate-RP messages are unicast to the BSR without any 2-way interaction
* and can thus be spoofed blindly(!) from anywhere in the internet.
*
* Everything else is on-link, multicast, or requires an adjacency - much
* harder to mess with.
*/
/* total number of RPs we keep information for */
static size_t bsr_max_rps = 1024;
DEFINE_MTYPE_STATIC(PIMD, PIM_BSR_CRP, "PIM BSR C-RP");
DEFINE_MTYPE_STATIC(PIMD, PIM_BSR_GROUP, "PIM BSR range");
DEFINE_MTYPE_STATIC(PIMD, PIM_BSR_ITEM, "PIM BSR C-RP range item");
static int rp_cmp(const struct bsr_crp_rp *a, const struct bsr_crp_rp *b)
{
return pim_addr_cmp(a->addr, b->addr);
}
DECLARE_RBTREE_UNIQ(bsr_crp_rps, struct bsr_crp_rp, item, rp_cmp);
static int group_cmp(const struct bsr_crp_group *a,
const struct bsr_crp_group *b)
{
return prefix_cmp(&a->range, &b->range);
}
DECLARE_RBTREE_UNIQ(bsr_crp_groups, struct bsr_crp_group, item, group_cmp);
static int r_g_cmp(const struct bsr_crp_item *a, const struct bsr_crp_item *b)
{
return prefix_cmp(&a->group->range, &b->group->range);
}
DECLARE_RBTREE_UNIQ(bsr_crp_rp_groups, struct bsr_crp_item, r_g_item, r_g_cmp);
static int g_r_cmp(const struct bsr_crp_item *a, const struct bsr_crp_item *b)
{
const struct bsr_crp_rp *rp_a = a->rp, *rp_b = b->rp;
/* NHT-failed RPs last */
if (rp_a->nht_ok > rp_b->nht_ok)
return -1;
if (rp_a->nht_ok < rp_b->nht_ok)
return 1;
/* This function determines BSR policy in what subset of the received
* RP candidates to advertise. The BSR is free to make its choices
* any way it deems useful
*/
/* lower numeric values are better */
if (rp_a->prio < rp_b->prio)
return -1;
if (rp_a->prio > rp_b->prio)
return 1;
/* prefer older RP for less churn */
if (rp_a->seen_first < rp_b->seen_first)
return -1;
if (rp_a->seen_first > rp_b->seen_first)
return 1;
return pim_addr_cmp(rp_a->addr, rp_b->addr);
}
DECLARE_RBTREE_UNIQ(bsr_crp_group_rps, struct bsr_crp_item, g_r_item, g_r_cmp);
void pim_bsm_generate(struct bsm_scope *scope)
{
struct bsm_frag *frag;
struct bsm_hdr *hdr;
bool have_dead = false;
assertf(scope->state == BSR_ELECTED, "state=%d", scope->state);
pim_bsm_frags_free(scope);
struct bsr_crp_group *group;
struct bsr_crp_item *item;
struct bsr_crp_rp *rp;
size_t n_groups = 0, n_rps = 0;
frr_each (bsr_crp_groups, scope->ebsr_groups, group) {
if (group->n_selected == 0) {
if (group->dead_count >= PIM_BSR_DEAD_COUNT)
continue;
have_dead = true;
} else
group->dead_count = 0;
n_groups++;
n_rps += group->n_selected;
}
if (PIM_DEBUG_BSM)
zlog_debug("Generating BSM (%zu ranges, %zu RPs)", n_groups, n_rps);
size_t datalen = PIM_MSG_HEADER_LEN + sizeof(*hdr) +
n_groups * sizeof(struct bsmmsg_grpinfo) +
n_rps * sizeof(struct bsmmsg_rpinfo);
frag = XCALLOC(MTYPE_PIM_BSM_FRAG, sizeof(*frag) + datalen);
uint8_t *pos = frag->data + PIM_MSG_HEADER_LEN;
uint8_t *end = frag->data + datalen;
hdr = (struct bsm_hdr *)pos;
pos += sizeof(*hdr);
assert(pos <= end);
/* TODO: make BSR hashmasklen configurable */
#if PIM_IPV == 6
hdr->hm_len = 126;
#else
hdr->hm_len = 30;
#endif
hdr->bsr_prio = scope->current_bsr_prio;
hdr->bsr_addr.family = PIM_IANA_AFI;
hdr->bsr_addr.reserved = 0;
hdr->bsr_addr.addr = scope->bsr_addrsel.run_addr;
frr_each (bsr_crp_groups, scope->ebsr_groups, group) {
if (group->n_selected == 0 &&
group->dead_count >= PIM_BSR_DEAD_COUNT)
continue;
struct bsmmsg_grpinfo *gi = (struct bsmmsg_grpinfo *)pos;
pos += sizeof(*gi);
assert(pos <= end);
gi->group.family = PIM_MSG_ADDRESS_FAMILY;
gi->group.mask = group->range.prefixlen;
gi->group.addr = group->range.prefix;
size_t n_added = 0;
frr_each (bsr_crp_group_rps, group->rps, item) {
if (!item->selected)
break;
struct bsmmsg_rpinfo *ri = (struct bsmmsg_rpinfo *)pos;
pos += sizeof(*ri);
assert(pos <= end);
rp = item->rp;
ri->rpaddr.family = PIM_MSG_ADDRESS_FAMILY;
ri->rpaddr.addr = rp->addr;
ri->rp_holdtime = htons(rp->holdtime);
ri->rp_pri = rp->prio;
n_added++;
}
gi->rp_count = group->n_selected;
gi->frag_rp_count = n_added;
assert(n_added == group->n_selected);
}
assertf(pos == end, "end-pos=%td", end - pos);
frag->size = datalen;
bsm_frags_add_head(scope->bsm_frags, frag);
scope->ebsr_have_dead_pending = have_dead;
/*
* The BSR itself doesn't receive (no loopback) the BSM msgs advertising
* the rps. Install the rps directly for the local BSR node.
*/
pim_bsm_parse_install_g2rp(scope, ((uint8_t *) hdr) + PIM_BSM_HDR_LEN,
datalen - PIM_BSM_HDR_LEN - PIM_MSG_HEADER_LEN, scope->bsm_frag_tag);
pim_bsm_changed(scope);
}
static void pim_bsm_generate_timer(struct event *t)
{
struct bsm_scope *scope = EVENT_ARG(t);
pim_bsm_generate(scope);
}
static void pim_bsm_generate_sched(struct bsm_scope *scope)
{
assertf(scope->state == BSR_ELECTED, "state=%d", scope->state);
if (scope->t_ebsr_regen_bsm)
return;
event_add_timer(router->master, pim_bsm_generate_timer, scope, 1,
&scope->t_ebsr_regen_bsm);
}
void pim_bsm_sent(struct bsm_scope *scope)
{
struct bsr_crp_group *group;
bool have_dead = false, changed = false;
if (!scope->ebsr_have_dead_pending)
return;
frr_each_safe (bsr_crp_groups, scope->ebsr_groups, group) {
if (group->n_selected != 0)
continue;
if (group->dead_count < PIM_BSR_DEAD_COUNT) {
group->dead_count++;
have_dead = true;
continue;
}
changed = true;
if (bsr_crp_group_rps_count(group->rps))
/* have RPs, but none selected */
continue;
/* no reason to keep this range anymore */
bsr_crp_groups_del(scope->ebsr_groups, group);
bsr_crp_group_rps_fini(group->rps);
XFREE(MTYPE_PIM_BSR_GROUP, group);
continue;
}
scope->ebsr_have_dead_pending = have_dead;
if (changed)
pim_bsm_generate_sched(scope);
}
static void bsr_crp_reselect(struct bsm_scope *scope,
struct bsr_crp_group *group)
{
bool changed = false;
struct bsr_crp_item *item;
size_t n_selected = 0;
frr_each (bsr_crp_group_rps, group->rps, item) {
bool select = false;
/* hardcode best 2 RPs for now */
if (item->rp->nht_ok && n_selected < 2) {
select = true;
n_selected++;
}
if (item->selected != select) {
changed = true;
item->selected = select;
}
}
changed |= group->deleted_selected;
group->deleted_selected = false;
group->n_selected = n_selected;
if (changed)
pim_bsm_generate_sched(scope);
scope->elec_rp_data_changed |= changed;
}
/* changing rp->nht_ok or rp->prio affects the sort order in group->rp
* lists, so need a delete & re-add if either changes
*/
static void pim_crp_nht_prio_change(struct bsr_crp_rp *rp, bool nht_ok,
uint8_t prio)
{
struct bsr_crp_item *item;
frr_each (bsr_crp_rp_groups, rp->groups, item)
bsr_crp_group_rps_del(item->group->rps, item);
rp->prio = prio;
rp->nht_ok = nht_ok;
frr_each (bsr_crp_rp_groups, rp->groups, item) {
bsr_crp_group_rps_add(item->group->rps, item);
bsr_crp_reselect(rp->scope, item->group);
}
}
static struct bsr_crp_group *group_get(struct bsm_scope *scope,
prefix_pim *range)
{
struct bsr_crp_group *group, ref;
ref.range = *range;
group = bsr_crp_groups_find(scope->ebsr_groups, &ref);
if (!group) {
group = XCALLOC(MTYPE_PIM_BSR_GROUP, sizeof(*group));
group->range = *range;
bsr_crp_group_rps_init(group->rps);
bsr_crp_groups_add(scope->ebsr_groups, group);
}
return group;
}
static void pim_crp_update(struct bsr_crp_rp *rp, struct cand_rp_msg *msg,
size_t ngroups)
{
struct bsr_crp_rp_groups_head oldgroups[1];
struct bsr_crp_item *item, itemref;
struct bsr_crp_group *group, groupref;
//struct bsm_scope *scope = rp->scope;
bsr_crp_rp_groups_init(oldgroups);
bsr_crp_rp_groups_swap_all(rp->groups, oldgroups);
itemref.rp = rp;
itemref.group = &groupref;
assert(msg || ngroups == 0);
for (size_t i = 0; i < ngroups; i++) {
if (msg->groups[i].family != PIM_MSG_ADDRESS_FAMILY)
continue;
if (msg->groups[i].bidir)
continue;
prefix_pim pfx;
pfx.family = PIM_AF;
pfx.prefixlen = msg->groups[i].mask;
pfx.prefix = msg->groups[i].addr;
#if PIM_IPV == 4
if (pfx.prefixlen < 4)
continue;
if (!IPV4_CLASS_DE(ntohl(pfx.prefix.s_addr)))
continue;
#endif
apply_mask(&pfx);
groupref.range = pfx;
item = bsr_crp_rp_groups_find(oldgroups, &itemref);
if (item) {
bsr_crp_rp_groups_del(oldgroups, item);
bsr_crp_rp_groups_add(rp->groups, item);
continue;
}
group = group_get(rp->scope, &pfx);
item = XCALLOC(MTYPE_PIM_BSR_ITEM, sizeof(*item));
item->rp = rp;
item->group = group;
bsr_crp_group_rps_add(group->rps, item);
bsr_crp_rp_groups_add(rp->groups, item);
bsr_crp_reselect(rp->scope, group);
}
while ((item = bsr_crp_rp_groups_pop(oldgroups))) {
group = item->group;
if (item->selected)
group->deleted_selected = true;
bsr_crp_group_rps_del(group->rps, item);
XFREE(MTYPE_PIM_BSR_ITEM, item);
bsr_crp_reselect(rp->scope, group);
}
bsr_crp_rp_groups_fini(oldgroups);
if (msg && msg->rp_prio != rp->prio)
pim_crp_nht_prio_change(rp, rp->nht_ok, msg->rp_prio);
}
void pim_crp_nht_update(struct pim_instance *pim, struct pim_nexthop_cache *pnc)
{
struct bsm_scope *scope = &pim->global_scope;
struct bsr_crp_rp *rp, ref;
bool ok;
ref.addr = pnc->rpf.rpf_addr;
rp = bsr_crp_rps_find(scope->ebsr_rps, &ref);
assertf(rp, "addr=%pPA", &ref.addr);
ok = CHECK_FLAG(pnc->flags, PIM_NEXTHOP_VALID);
if (ok == rp->nht_ok)
return;
if (PIM_DEBUG_BSM)
zlog_debug("Candidate-RP %pPA NHT %s", &rp->addr, ok ? "UP" : "DOWN");
pim_crp_nht_prio_change(rp, ok, rp->prio);
}
static void pim_crp_free(struct pim_instance *pim, struct bsr_crp_rp *rp)
{
EVENT_OFF(rp->t_hold);
pim_nht_candrp_del(pim, rp->addr);
bsr_crp_rp_groups_fini(rp->groups);
XFREE(MTYPE_PIM_BSR_CRP, rp);
}
static void pim_crp_expire(struct event *t)
{
struct bsr_crp_rp *rp = EVENT_ARG(t);
struct pim_instance *pim = rp->scope->pim;
if (PIM_DEBUG_BSM)
zlog_debug("Candidate-RP %pPA holdtime expired", &rp->addr);
pim_crp_update(rp, NULL, 0);
bsr_crp_rps_del(rp->scope->ebsr_rps, rp);
pim_crp_free(pim, rp);
}
int pim_crp_process(struct interface *ifp, pim_sgaddr *src_dst, uint8_t *buf,
uint32_t buf_size)
{
struct pim_interface *pim_ifp = NULL;
struct pim_instance *pim;
struct bsm_scope *scope;
pim_ifp = ifp->info;
if (!pim_ifp) {
if (PIM_DEBUG_BSM)
zlog_debug("%s: multicast not enabled on interface %s",
__func__, ifp->name);
return -1;
}
//pim_ifp->pim_ifstat_bsm_rx++;
pim = pim_ifp->pim;
//pim->bsm_rcvd++;
if (!pim_ifp->bsm_enable) {
zlog_warn("%s: BSM not enabled on interface %s", __func__,
ifp->name);
//pim_ifp->pim_ifstat_bsm_cfg_miss++;
//pim->bsm_dropped++;
return -1;
}
if (buf_size < (PIM_MSG_HEADER_LEN + sizeof(struct cand_rp_msg))) {
if (PIM_DEBUG_BSM)
zlog_debug("%s: received buffer length of %d which is too small to properly decode",
__func__, buf_size);
return -1;
}
scope = &pim->global_scope;
if (scope->state < BSR_PENDING) {
if (PIM_DEBUG_BSM)
zlog_debug("received Candidate-RP message from %pPA while not BSR",
&src_dst->src);
return -1;
}
size_t remain = buf_size;
struct cand_rp_msg *crp_hdr;
buf += PIM_MSG_HEADER_LEN;
remain -= PIM_MSG_HEADER_LEN;
crp_hdr = (struct cand_rp_msg *)buf;
buf += sizeof(*crp_hdr);
remain -= sizeof(*crp_hdr);
size_t ngroups = crp_hdr->prefix_cnt;
if (remain < ngroups * sizeof(struct pim_encoded_group_ipv4)) {
if (PIM_DEBUG_BSM)
zlog_debug("truncated Candidate-RP advertisement for RP %pPA from %pPA (too short for %zu groups)",
(pim_addr *)&crp_hdr->rp_addr.addr,
&src_dst->src, ngroups);
return -1;
}
if (PIM_DEBUG_BSM)
zlog_debug("Candidate-RP: %pPA, prio=%u (from %pPA, %zu groups)",
(pim_addr *)&crp_hdr->rp_addr.addr, crp_hdr->rp_prio,
&src_dst->src, ngroups);
struct bsr_crp_rp *rp, ref;
ref.addr = crp_hdr->rp_addr.addr;
rp = bsr_crp_rps_find(scope->ebsr_rps, &ref);
if (!rp) {
if (bsr_crp_rps_count(scope->ebsr_rps) >= bsr_max_rps) {
zlog_err("BSR: number of tracked Candidate RPs (%zu) exceeds DoS-protection limit (%zu), dropping advertisement for RP %pPA (packet source %pPA)",
bsr_crp_rps_count(scope->ebsr_rps),
bsr_max_rps, (pim_addr *)&crp_hdr->rp_addr.addr,
&src_dst->src);
return -1;
}
if (PIM_DEBUG_BSM)
zlog_debug("new Candidate-RP: %pPA (from %pPA)",
(pim_addr *)&crp_hdr->rp_addr.addr,
&src_dst->src);
rp = XCALLOC(MTYPE_PIM_BSR_CRP, sizeof(*rp));
rp->scope = scope;
rp->addr = crp_hdr->rp_addr.addr;
rp->prio = 255;
bsr_crp_rp_groups_init(rp->groups);
rp->seen_first = monotime(NULL);
bsr_crp_rps_add(scope->ebsr_rps, rp);
rp->nht_ok = pim_nht_candrp_add(pim, rp->addr);
}
rp->seen_last = monotime(NULL);
rp->holdtime = ntohs(crp_hdr->rp_holdtime);
EVENT_OFF(rp->t_hold);
event_add_timer(router->master, pim_crp_expire, rp,
ntohs(crp_hdr->rp_holdtime), &rp->t_hold);
pim_crp_update(rp, crp_hdr, ngroups);
return 0;
}
void pim_crp_db_clear(struct bsm_scope *scope)
{
struct bsr_crp_rp *rp;
struct bsr_crp_group *group;
struct bsr_crp_item *item;
while ((rp = bsr_crp_rps_pop(scope->ebsr_rps))) {
while ((item = bsr_crp_rp_groups_pop(rp->groups))) {
group = item->group;
if (item->selected)
group->deleted_selected = true;
bsr_crp_group_rps_del(group->rps, item);
XFREE(MTYPE_PIM_BSR_ITEM, item);
}
pim_crp_free(scope->pim, rp);
}
while ((group = bsr_crp_groups_pop(scope->ebsr_groups))) {
assertf(!bsr_crp_group_rps_count(group->rps),
"range=%pFX rp_count=%zu", &group->range,
bsr_crp_group_rps_count(group->rps));
bsr_crp_group_rps_fini(group->rps);
XFREE(MTYPE_PIM_BSR_GROUP, group);
}
}
int pim_crp_db_show(struct vty *vty, struct bsm_scope *scope)
{
struct bsr_crp_rp *rp;
struct bsr_crp_item *item;
vty_out(vty, "RP/Group NHT Prio Uptime Hold\n");
frr_each (bsr_crp_rps, scope->ebsr_rps, rp) {
vty_out(vty, "%-15pPA %4s %4u %8ld %4lu\n", &rp->addr,
rp->nht_ok ? "UP" : "DOWN", rp->prio,
(long)(monotime(NULL) - rp->seen_first),
event_timer_remain_second(rp->t_hold));
frr_each (bsr_crp_rp_groups, rp->groups, item)
vty_out(vty, "%c %-18pFX\n", item->selected ? '>' : ' ',
&item->group->range);
}
return CMD_SUCCESS;
}
int pim_crp_groups_show(struct vty *vty, struct bsm_scope *scope)
{
struct bsr_crp_group *group;
struct bsr_crp_item *item;
if (scope->ebsr_have_dead_pending)
vty_out(vty, "have_dead_pending\n");
frr_each (bsr_crp_groups, scope->ebsr_groups, group) {
vty_out(vty, "%c %pFX", group->n_selected ? '^' : '!',
&group->range);
if (group->n_selected == 0)
vty_out(vty, " (dead %u)", group->dead_count);
vty_out(vty, "\n");
frr_each (bsr_crp_group_rps, group->rps, item)
vty_out(vty, "%c %pPA\n", item->selected ? '>' : ' ',
&item->rp->addr);
}
return CMD_SUCCESS;
}

View file

@ -2937,6 +2937,54 @@ DEFUN (show_ip_pim_cand_rp,
return CMD_SUCCESS;
}
DEFUN (show_ip_pim_bsr_rpdb,
show_ip_pim_bsr_rpdb_cmd,
"show ip pim bsr candidate-rps [vrf NAME] [json]",
SHOW_STR
IP_STR
PIM_STR
"boot-strap router information\n"
"Candidate RPs\n"
VRF_CMD_HELP_STR
JSON_STR)
{
int idx = 2;
struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
//bool uj = use_json(argc, argv);
if (!vrf || !vrf->info)
return CMD_WARNING;
struct pim_instance *pim = vrf->info;
struct bsm_scope *scope = &pim->global_scope;
return pim_crp_db_show(vty, scope);
}
DEFUN (show_ip_pim_bsr_groups,
show_ip_pim_bsr_groups_cmd,
"show ip pim bsr groups [vrf NAME] [json]",
SHOW_STR
IP_STR
PIM_STR
"boot-strap router information\n"
"Candidate RP groups\n"
VRF_CMD_HELP_STR
JSON_STR)
{
int idx = 2;
struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
//bool uj = use_json(argc, argv);
if (!vrf || !vrf->info)
return CMD_WARNING;
struct pim_instance *pim = vrf->info;
struct bsm_scope *scope = &pim->global_scope;
return pim_crp_groups_show(vty, scope);
}
DEFPY (show_ip_pim_statistics,
show_ip_pim_statistics_cmd,
"show ip pim [vrf NAME] statistics [interface WORD$word] [json$json]",
@ -4436,6 +4484,27 @@ DEFPY_ATTR(no_ip_pim_rp_prefix_list,
return ret;
}
DEFPY (pim_bsr_candidate_bsr,
pim_bsr_candidate_bsr_cmd,
"[no] bsr candidate-bsr [{priority (0-255)|source <address A.B.C.D|interface IFNAME|loopback$loopback|any$any>}]",
NO_STR
BSR_STR
"Make this router a Candidate BSR\n"
"BSR Priority (higher wins)\n"
"BSR Priority (higher wins)\n"
"Specify IP address for BSR 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_BSR_XPATH, no,
false, any, ifname, address_str,
priority_str, NULL);
}
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>}]",
@ -8647,6 +8716,7 @@ void pim_cmd_init(void)
install_element(PIM_NODE, &pim_bsr_candidate_rp_cmd);
install_element(PIM_NODE, &pim_bsr_candidate_rp_group_cmd);
install_element(PIM_NODE, &pim_bsr_candidate_bsr_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_cmd);
install_element(INTERFACE_NODE, &interface_no_ip_igmp_cmd);
@ -8769,6 +8839,8 @@ void pim_cmd_init(void)
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_bsr_rpdb_cmd);
install_element(VIEW_NODE, &show_ip_pim_bsr_groups_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);

View file

@ -24,7 +24,6 @@
#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"

View file

@ -5237,6 +5237,12 @@ void pim_show_bsr(struct pim_instance *pim, struct vty *vty, bool uj)
case ACCEPT_PREFERRED:
strlcpy(bsr_state, "ACCEPT_PREFERRED", sizeof(bsr_state));
break;
case BSR_PENDING:
strlcpy(bsr_state, "BSR_PENDING", sizeof(bsr_state));
break;
case BSR_ELECTED:
strlcpy(bsr_state, "BSR_ELECTED", sizeof(bsr_state));
break;
default:
strlcpy(bsr_state, "", sizeof(bsr_state));
}

View file

@ -7,6 +7,8 @@
#ifndef PIM_CMD_COMMON_H
#define PIM_CMD_COMMON_H
#define BSR_STR "Bootstrap Router configuration\n"
struct pim_upstream;
struct pim_instance;

View file

@ -253,6 +253,7 @@ void pim_vrf_terminate(void)
if (!pim)
continue;
pim_crp_db_clear(&pim->global_scope);
pim_ssmpingd_destroy(pim);
pim_instance_terminate(pim);

View file

@ -148,6 +148,7 @@ struct pim_encoded_source_ipv6 {
typedef struct pim_encoded_ipv4_unicast pim_encoded_unicast;
typedef struct pim_encoded_group_ipv4 pim_encoded_group;
typedef struct pim_encoded_source_ipv4 pim_encoded_source;
#define PIM_MSG_ADDRESS_FAMILY PIM_MSG_ADDRESS_FAMILY_IPV4
typedef struct ip ipv_hdr;
#define IPV_SRC(ip_hdr) ((ip_hdr))->ip_src
#define IPV_DST(ip_hdr) ((ip_hdr))->ip_dst
@ -156,6 +157,7 @@ typedef struct ip ipv_hdr;
typedef struct pim_encoded_ipv6_unicast pim_encoded_unicast;
typedef struct pim_encoded_group_ipv6 pim_encoded_group;
typedef struct pim_encoded_source_ipv6 pim_encoded_source;
#define PIM_MSG_ADDRESS_FAMILY PIM_MSG_ADDRESS_FAMILY_IPV6
typedef struct ip6_hdr ipv_hdr;
#define IPV_SRC(ip_hdr) ((ip_hdr))->ip6_src
#define IPV_DST(ip_hdr) ((ip_hdr))->ip6_dst

View file

@ -388,6 +388,48 @@ 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 = {
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-bsr",
.cbs = {
.create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_create,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-bsr/bsr-priority",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_priority_modify,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-bsr/address",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_modify,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-bsr/interface",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_modify,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-bsr/if-loopback",
.cbs = {
.create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_create,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-bsr/if-any",
.cbs = {
.create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_create,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_destroy,
}
},
/* Candidate-RP */
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-candidate:candidate-rp",

View file

@ -160,6 +160,20 @@ 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-cand-bsr */
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_create(
struct nb_cb_create_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_destroy(
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_priority_modify(
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_create(
struct nb_cb_create_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_modify(
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_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);
@ -225,6 +239,9 @@ int routing_control_plane_protocols_name_validate(
#define FRR_PIM_AF_XPATH_VAL "frr-routing:ipv6"
#endif
#define FRR_PIM_CAND_RP_XPATH "./frr-pim-candidate:candidate-rp"
#define FRR_PIM_CAND_BSR_XPATH "./frr-pim-candidate:candidate-bsr"
#define FRR_PIM_VRF_XPATH \
"/frr-routing:routing/control-plane-protocols/" \
"control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \
@ -240,8 +257,6 @@ 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 \

View file

@ -2692,6 +2692,152 @@ static void yang_addrsel(struct cand_addrsel *addrsel,
}
}
static int candidate_bsr_addrsel(struct bsm_scope *scope,
const struct lyd_node *cand_bsr_node)
{
yang_addrsel(&scope->bsr_addrsel, cand_bsr_node);
pim_cand_bsr_apply(scope);
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_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->bsr_addrsel.cfg_enable = true;
scope->cand_bsr_prio = yang_dnode_get_uint8(args->dnode,
"bsr-priority");
candidate_bsr_addrsel(scope, args->dnode);
break;
}
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_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->bsr_addrsel.cfg_enable = false;
pim_cand_bsr_apply(scope);
break;
}
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_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_bsr_prio = yang_dnode_get_uint8(args->dnode, NULL);
/* FIXME: force prio update */
candidate_bsr_addrsel(scope, args->dnode);
break;
}
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_create(
struct nb_cb_create_args *args)
{
struct vrf *vrf;
struct pim_instance *pim;
struct bsm_scope *scope;
const struct lyd_node *cand_bsr_node;
cand_bsr_node = yang_dnode_get_parent(args->dnode, "candidate-bsr");
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;
return candidate_bsr_addrsel(scope, cand_bsr_node);
}
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_modify(
struct nb_cb_modify_args *args)
{
struct vrf *vrf;
struct pim_instance *pim;
struct bsm_scope *scope;
const struct lyd_node *cand_bsr_node;
cand_bsr_node = yang_dnode_get_parent(args->dnode, "candidate-bsr");
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;
return candidate_bsr_addrsel(scope, cand_bsr_node);
}
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_addrsel_destroy(
struct nb_cb_destroy_args *args)
{
/* nothing to do here, we'll get a CREATE for something else */
return NB_OK;
}
static int candidate_rp_addrsel(struct bsm_scope *scope,
const struct lyd_node *cand_rp_node)
{

View file

@ -139,7 +139,7 @@ static bool pim_pkt_dst_addr_ok(enum pim_msg_type type, pim_addr addr)
}
int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len,
pim_sgaddr sg)
pim_sgaddr sg, bool is_mcast)
{
struct iovec iov[2], *iovp = iov;
#if PIM_IPV == 4
@ -274,6 +274,21 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len,
return -1;
}
if (!is_mcast) {
if (header->type == PIM_MSG_TYPE_CANDIDATE) {
if (PIM_DEBUG_PIM_PACKETS)
zlog_debug( "%s %s: Candidate RP PIM message from %pPA on %s",
__FILE__, __func__, &sg.src, ifp->name);
return pim_crp_process(ifp, &sg, pim_msg, pim_msg_len);
}
if (PIM_DEBUG_PIM_PACKETS)
zlog_debug(
"ignoring link traffic on BSR unicast socket");
return -1;
}
switch (header->type) {
case PIM_MSG_TYPE_HELLO:
return pim_hello_recv(ifp, sg.src, pim_msg + PIM_MSG_HEADER_LEN,
@ -322,6 +337,13 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len,
return pim_bsm_process(ifp, &sg, pim_msg, pim_msg_len, no_fwd);
break;
case PIM_MSG_TYPE_CANDIDATE:
/* return pim_crp_process(ifp, &sg, pim_msg, pim_msg_len); */
if (PIM_DEBUG_PIM_PACKETS)
zlog_debug(
"ignoring Candidate-RP packet on multicast socket");
return 0;
default:
if (PIM_DEBUG_PIM_PACKETS) {
zlog_debug(
@ -395,7 +417,7 @@ static void pim_sock_read(struct event *t)
sg.grp = ((struct sockaddr_in6 *)&to)->sin6_addr;
#endif
int fail = pim_pim_packet(ifp, buf, len, sg);
int fail = pim_pim_packet(ifp, buf, len, sg, true);
if (fail) {
if (PIM_DEBUG_PIM_PACKETS)
zlog_debug("%s: pim_pim_packet() return=%d",

View file

@ -42,7 +42,7 @@ void pim_hello_restart_now(struct interface *ifp);
void pim_hello_restart_triggered(struct interface *ifp);
int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len,
pim_sgaddr sg);
pim_sgaddr sg, bool is_mcast);
int pim_msg_send(int fd, pim_addr src, pim_addr dst, uint8_t *pim_msg,
int pim_msg_size, struct interface *ifp);

View file

@ -26,6 +26,7 @@ struct pim_instance;
int pim_socket_bind(int fd, struct interface *ifp);
void pim_socket_ip_hdr(int fd);
int pim_setsockopt_packetinfo(int fd);
int pim_socket_raw(int protocol);
int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp,
uint8_t loop);

View file

@ -19,12 +19,6 @@
#include "pim_iface.h"
#include "pim_addr.h"
#if PIM_IPV == 4
#define PIM_MSG_ADDRESS_FAMILY PIM_MSG_ADDRESS_FAMILY_IPV4
#else
#define PIM_MSG_ADDRESS_FAMILY PIM_MSG_ADDRESS_FAMILY_IPV6
#endif
uint8_t *pim_tlv_append_uint16(uint8_t *buf, const uint8_t *buf_pastend,
uint16_t option_type, uint16_t option_value)
{

View file

@ -17,6 +17,7 @@ pim_common = \
pimd/pim_assert.c \
pimd/pim_bfd.c \
pimd/pim_bsm.c \
pimd/pim_bsr_rpdb.c \
pimd/pim_cmd_common.c \
pimd/pim_errors.c \
pimd/pim_hello.c \
@ -162,12 +163,12 @@ clippy_scan += \
# end
pimd_pimd_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=4
pimd_pimd_LDADD = lib/libfrr.la $(LIBCAP)
pimd_pimd_LDADD = lib/libfrr.la $(LIBCAP) -lm
if PIM6D
sbin_PROGRAMS += pimd/pim6d
pimd_pim6d_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=6
pimd_pim6d_LDADD = lib/libfrr.la $(LIBCAP)
pimd_pim6d_LDADD = lib/libfrr.la $(LIBCAP) -lm
endif
pimd_test_igmpv3_join_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=4

View file

@ -71,6 +71,43 @@ module frr-pim-candidate {
/*
* Groupings
*/
grouping candidate-bsr-container {
description
"Grouping of Candidate BSR settings.";
container candidate-bsr {
presence
"Enable router to be a Candidate BSR.";
description
"Candidate BSR settings";
leaf bsr-priority {
type uint8;
default "64";
description
"BSR priority for this router, higher values win.";
}
choice source-address-or-interface {
description "IP address to use for BSR 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;
}
}
} // candidate-bsr
} // candidate-bsr-container
grouping candidate-rp-container {
description
"Grouping of Candidate RP settings.";
@ -131,6 +168,7 @@ module frr-pim-candidate {
+ "frr-pim:address-family" {
description "PIM Candidate RP augmentation.";
uses candidate-bsr-container;
uses candidate-rp-container;
}
}