Skip to content

Commit

Permalink
pimd: MSDP SA loop check using AS Path
Browse files Browse the repository at this point in the history
Implement the 'RFC 4611 Section 2.1. Peering between PIM Border Routers'
recommendation of using the peer AS number to check for loops in the
topology when using direct inter-domains connections.

Signed-off-by: Rafael Zalamena <[email protected]>
  • Loading branch information
rzalamena committed Dec 23, 2024
1 parent 0287e3d commit 434816a
Show file tree
Hide file tree
Showing 10 changed files with 261 additions and 8 deletions.
31 changes: 26 additions & 5 deletions pimd/pim_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -6778,19 +6778,26 @@ ALIAS(no_ip_pim_bfd, no_ip_pim_bfd_param_cmd,
#endif /* !HAVE_BFDD */

DEFPY(pim_msdp_peer, pim_msdp_peer_cmd,
"msdp peer A.B.C.D$peer source A.B.C.D$source",
"msdp peer A.B.C.D$peer source A.B.C.D$source [as (1-4294967295)$asn]",
CFG_MSDP_STR
"Configure MSDP peer\n"
"Peer IP address\n"
"Source address for TCP connection\n"
"Local IP address\n")
"Local IP address\n"
"BGP Autonomous System peer information\n"
"BGP Autonomous System peer number\n")
{
char msdp_peer_source_xpath[XPATH_MAXLEN];

snprintf(msdp_peer_source_xpath, sizeof(msdp_peer_source_xpath),
"./msdp-peer[peer-ip='%s']/source-ip", peer_str);
nb_cli_enqueue_change(vty, msdp_peer_source_xpath, NB_OP_MODIFY,
source_str);
if (asn_str) {
snprintf(msdp_peer_source_xpath, sizeof(msdp_peer_source_xpath),
"./msdp-peer[peer-ip='%s']/as", peer_str);
nb_cli_enqueue_change(vty, msdp_peer_source_xpath, NB_OP_MODIFY, asn_str);
}

return nb_cli_apply_changes(vty, NULL);
}
Expand Down Expand Up @@ -7795,7 +7802,7 @@ static void ip_msdp_show_peers(struct pim_instance *pim, struct vty *vty,
json = json_object_new_object();
} else {
vty_out(vty,
"Peer Local State Uptime SaCnt\n");
"Peer Local State Uptime SaCnt AS\n");
}

for (ALL_LIST_ELEMENTS_RO(pim->msdp.peer_list, mpnode, mp)) {
Expand All @@ -7817,10 +7824,17 @@ static void ip_msdp_show_peers(struct pim_instance *pim, struct vty *vty,
json_object_string_add(json_row, "state", state_str);
json_object_string_add(json_row, "upTime", timebuf);
json_object_int_add(json_row, "saCount", mp->sa_cnt);
if (mp->asn)
json_object_int_add(json_row, "asn", mp->asn);

json_object_object_add(json, peer_str, json_row);
} else {
vty_out(vty, "%-15s %15s %11s %8s %6d\n", peer_str,
local_str, state_str, timebuf, mp->sa_cnt);
vty_out(vty, "%-15s %15s %11s %8s %6d", peer_str, local_str, state_str,
timebuf, mp->sa_cnt);
if (mp->asn)
vty_out(vty, " %5d\n", mp->asn);
else
vty_out(vty, " %5s\n", "-");
}
}

Expand Down Expand Up @@ -8093,6 +8107,7 @@ static void ip_msdp_show_sa_entry_detail(struct pim_msdp_sa *sa,
char spt_str[8];
char local_str[8];
char statetimer[PIM_MSDP_TIMER_STRLEN];
uint32_t asn = pim_msdp_sa_asn(sa);
int64_t now;
json_object *json_group = NULL;
json_object *json_row = NULL;
Expand Down Expand Up @@ -8135,6 +8150,9 @@ static void ip_msdp_show_sa_entry_detail(struct pim_msdp_sa *sa,
json_object_string_add(json_row, "sptSetup", spt_str);
json_object_string_add(json_row, "upTime", timebuf);
json_object_string_add(json_row, "stateTimer", statetimer);
if (asn)
json_object_int_add(json_row, "asn", asn);

json_object_object_add(json_group, src_str, json_row);
} else {
vty_out(vty, "SA : %s\n", sa->sg_str);
Expand All @@ -8144,6 +8162,9 @@ static void ip_msdp_show_sa_entry_detail(struct pim_msdp_sa *sa,
vty_out(vty, " SPT Setup : %s\n", spt_str);
vty_out(vty, " Uptime : %s\n", timebuf);
vty_out(vty, " State Timer : %s\n", statetimer);
if (asn)
vty_out(vty, " BGP/AS : %u\n", asn);

vty_out(vty, "\n");
}
}
Expand Down
18 changes: 15 additions & 3 deletions pimd/pim_msdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ void pim_msdp_originator_id(struct pim_instance *pim, const struct prefix *group
}
}

uint32_t pim_msdp_sa_asn(const struct pim_msdp_sa *sa)
{
struct pim_msdp_peer *peer = pim_msdp_peer_find(sa->pim, sa->peer);

if (peer == NULL)
return 0;

return peer->asn;
}

/************************ SA cache management ******************************/
/* RFC-3618:Sec-5.1 - global active source advertisement timer */
static void pim_msdp_sa_adv_timer_cb(struct event *t)
Expand Down Expand Up @@ -706,7 +716,7 @@ bool pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp)
}

/* check if the MSDP peer is the nexthop for the RP */
if (pim_nht_lookup(mp->pim, &nexthop, rp, 0) &&
if (pim_route_lookup(mp->pim, rp, mp->asn, &nexthop) &&
nexthop.mrib_nexthop_addr.s_addr == mp->peer.s_addr) {
return true;
}
Expand Down Expand Up @@ -1332,8 +1342,10 @@ bool pim_msdp_peer_config_write(struct vty *vty, struct pim_instance *pim)
if (mp->flags & PIM_MSDP_PEERF_IN_GROUP)
continue;

vty_out(vty, " msdp peer %pI4 source %pI4\n", &mp->peer,
&mp->local);
vty_out(vty, " msdp peer %pI4 source %pI4", &mp->peer, &mp->local);
if (mp->asn)
vty_out(vty, " as %u", mp->asn);
vty_out(vty, "\n");

if (mp->auth_type == MSDP_AUTH_MD5)
vty_out(vty, " msdp peer %pI4 password %s\n", &mp->peer,
Expand Down
4 changes: 4 additions & 0 deletions pimd/pim_msdp.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ struct pim_msdp_peer {

/** SA maximum amount. */
uint32_t sa_limit;

/** BGP AS number for RPF check. */
uint32_t asn;
};

struct pim_msdp_mg_mbr {
Expand Down Expand Up @@ -253,6 +256,7 @@ void pim_msdp_sa_ref(struct pim_instance *pim, struct pim_msdp_peer *mp,
pim_sgaddr *sg, struct in_addr rp);
void pim_msdp_sa_local_update(struct pim_upstream *up);
void pim_msdp_sa_local_del(struct pim_instance *pim, pim_sgaddr *sg);
uint32_t pim_msdp_sa_asn(const struct pim_msdp_sa *sa);
void pim_msdp_i_am_rp_changed(struct pim_instance *pim);
bool pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp);
void pim_msdp_up_join_state_changed(struct pim_instance *pim,
Expand Down
7 changes: 7 additions & 0 deletions pimd/pim_nb.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,13 @@ const struct frr_yang_module_info frr_pim_info = {
.destroy = pim_msdp_peer_sa_limit_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/as",
.cbs = {
.modify = pim_msdp_peer_as_modify,
.destroy = pim_msdp_peer_as_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/mlag",
.cbs = {
Expand Down
2 changes: 2 additions & 0 deletions pimd/pim_nb.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ int pim_msdp_peer_authentication_key_modify(struct nb_cb_modify_args *args);
int pim_msdp_peer_authentication_key_destroy(struct nb_cb_destroy_args *args);
int pim_msdp_peer_sa_limit_modify(struct nb_cb_modify_args *args);
int pim_msdp_peer_sa_limit_destroy(struct nb_cb_destroy_args *args);
int pim_msdp_peer_as_modify(struct nb_cb_modify_args *args);
int pim_msdp_peer_as_destroy(struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mlag_create(
struct nb_cb_create_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mlag_destroy(
Expand Down
43 changes: 43 additions & 0 deletions pimd/pim_nb_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,8 @@ pim6_msdp_err(pim_msdp_peer_sa_filter_out_modify, nb_cb_modify_args);
pim6_msdp_err(pim_msdp_peer_sa_filter_out_destroy, nb_cb_destroy_args);
pim6_msdp_err(pim_msdp_peer_sa_limit_modify, nb_cb_modify_args);
pim6_msdp_err(pim_msdp_peer_sa_limit_destroy, nb_cb_destroy_args);
pim6_msdp_err(pim_msdp_peer_as_modify, nb_cb_modify_args);
pim6_msdp_err(pim_msdp_peer_as_destroy, nb_cb_destroy_args);
pim6_msdp_err(
routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_peer_source_ip_modify,
nb_cb_modify_args);
Expand Down Expand Up @@ -1677,6 +1679,47 @@ int pim_msdp_peer_sa_limit_destroy(struct nb_cb_destroy_args *args)

return NB_OK;
}

/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/as
*/
int pim_msdp_peer_as_modify(struct nb_cb_modify_args *args)
{
struct pim_msdp_peer *peer;

switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
/* NOTHING */
break;
case NB_EV_APPLY:
peer = nb_running_get_entry(args->dnode, NULL, true);
peer->asn = yang_dnode_get_uint32(args->dnode, NULL);
break;
}

return NB_OK;
}

int pim_msdp_peer_as_destroy(struct nb_cb_destroy_args *args)
{
struct pim_msdp_peer *peer;

switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
/* NOTHING */
break;
case NB_EV_APPLY:
peer = nb_running_get_entry(args->dnode, NULL, true);
peer->asn = 0;
break;
}

return NB_OK;
}
#endif /* PIM_IPV != 6 */

/*
Expand Down
131 changes: 131 additions & 0 deletions pimd/pim_rpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,134 @@ int pim_rpf_is_same(struct pim_rpf *rpf1, struct pim_rpf *rpf2)

return 0;
}

bool pim_route_lookup(struct pim_instance *pim, pim_addr addr, uint32_t asn, struct pim_nexthop *pn)
{
struct interface *ifp;
struct zroute_info *ri;
struct zroute_nh_info *rni;
bool found = false;

/* Zero out pim next hop response.*/
memset(pn, 0, sizeof(*pn));

/* Search route information. */
ri = zclient_route_lookup(pim, &addr);
if (ri == NULL) {
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: could not find route for address %pPA", __func__, &addr);
return false;
}

/*
* AS check (in case AS number was provided).
*
* This code path checks for the following MSDP rule:
*
* > For a direct peering inter-domain environment to be successful, the
* > first AS in the MBGP best path to the originating RP should be the
* > same as the AS of the MSDP peer.
*
* RFC 4611 Section 2.1. Peering between PIM Border Routers.
*/
if (asn) {
long long first_asn;

/*
* We expect a BGP route so if no AS Path information is
* available it means this is not a BGP route.
*/
if (ri->ri_type != ZEBRA_ROUTE_BGP) {
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: expected BGP route for %pPA (got %d)", __func__,
&addr, ri->ri_type);
goto free_and_exit;
}

errno = 0;
first_asn = strtoll((char *)ri->ri_opaque, NULL, 10);
/* Check for number conversion failures. */
if (first_asn == LLONG_MIN || first_asn == LLONG_MAX) {
zlog_warn("%s: AS number overflow/underflow %s", __func__, ri->ri_opaque);
goto free_and_exit;
}
if (first_asn == 0 && errno != 0) {
zlog_warn("%s: AS number conversion failed: %s", __func__, strerror(errno));
goto free_and_exit;
}

/* AS did not match. */
if (first_asn != asn) {
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: next hop AS did not match for address %pPA (%u != %lld)",
__func__, &addr, asn, first_asn);
goto free_and_exit;
}

/* Proceed to validate next hop. */
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: next hop AS matched for address %pPA (AS %u)", __func__,
&addr, asn);
}

SLIST_FOREACH (rni, &ri->ri_nhlist, rni_entry) {
ifp = if_lookup_by_index(rni->rni_ifindex, pim->vrf->vrf_id);
if (ifp == NULL) {
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: could not find interface for ifindex %d (address %pPA)",
__func__, rni->rni_ifindex, &addr);
continue;
}
if (ifp->info == NULL) {
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %pPA)",
__func__, ifp->name, rni->rni_ifindex, &addr);
continue;
}

/* Fill next hop parameter and return. */
switch (rni->rni_type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: found nexthop %pI4 for address %pPA: interface %s ifindex=%d metric=%d pref=%d",
__func__, &rni->rni_addr.v4, &addr, ifp->name,
rni->rni_ifindex, ri->ri_metric, ri->ri_distance);

#if PIM_IPV == 4
pn->mrib_nexthop_addr = rni->rni_addr.v4;
#endif
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: found nexthop %pI6 for address %pPA: interface %s ifindex=%d metric=%d pref=%d",
__func__, &rni->rni_addr, &addr, ifp->name,
rni->rni_ifindex, ri->ri_metric, ri->ri_distance);

#if PIM_IPV == 6
pn->mrib_nexthop_addr = rni->rni_addr.v6;
#endif
break;
case NEXTHOP_TYPE_IFINDEX:
break;

default:
zlog_warn("%s: invalid next hop address type %d", __func__, rni->rni_type);
continue;
}

pn->interface = ifp;
pn->mrib_metric_preference = ri->ri_distance;
pn->mrib_route_metric = ri->ri_metric;
pn->last_lookup = addr;
pn->last_lookup_time = pim_time_monotonic_usec();

found = true;
break;
}

free_and_exit:
zroute_info_free(&ri);
return found;
}
1 change: 1 addition & 0 deletions pimd/pim_rpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,5 @@ int pim_rpf_addr_is_inaddr_any(struct pim_rpf *rpf);

int pim_rpf_is_same(struct pim_rpf *rpf1, struct pim_rpf *rpf2);
void pim_rpf_set_refresh_time(struct pim_instance *pim);
bool pim_route_lookup(struct pim_instance *pim, pim_addr addr, uint32_t asn, struct pim_nexthop *pn);
#endif /* PIM_RPF_H */
21 changes: 21 additions & 0 deletions pimd/pim_zlookup.c
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,27 @@ int zclient_lookup_nexthop(struct pim_instance *pim,
return -2;
}

struct zroute_info *zclient_route_lookup(struct pim_instance *pim, const pim_addr *addr)
{
if (zlookup->sock < 0) {
flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient lookup socket is not connected", __func__);
zclient_lookup_failed(zlookup);
return NULL;
}

if (pim->vrf->vrf_id == VRF_UNKNOWN) {
zlog_notice("%s: VRF: %s does not fully exist yet, delaying lookup", __func__,
pim->vrf->name);
return NULL;
}

#if PIM_IPV == 4
return zapi_route_lookup(zlookup, pim->vrf->vrf_id, AF_INET, addr);
#else
return zapi_route_lookup(zlookup, pim->vrf->vrf_id, AF_INET6, addr);
#endif
}

void pim_zlookup_show_ip_multicast(struct vty *vty)
{
vty_out(vty, "Zclient lookup socket: ");
Expand Down
Loading

0 comments on commit 434816a

Please sign in to comment.