From 434816a294946ba08bb6d9ae612beb5bbe964b8a Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Wed, 28 Apr 2021 12:54:07 -0300 Subject: [PATCH] pimd: MSDP SA loop check using AS Path 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 --- pimd/pim_cmd.c | 31 ++++++++-- pimd/pim_msdp.c | 18 +++++- pimd/pim_msdp.h | 4 ++ pimd/pim_nb.c | 7 +++ pimd/pim_nb.h | 2 + pimd/pim_nb_config.c | 43 ++++++++++++++ pimd/pim_rpf.c | 131 +++++++++++++++++++++++++++++++++++++++++++ pimd/pim_rpf.h | 1 + pimd/pim_zlookup.c | 21 +++++++ pimd/pim_zlookup.h | 11 ++++ 10 files changed, 261 insertions(+), 8 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index a34fb344feb5..f30fa6d379bd 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -6778,12 +6778,14 @@ 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]; @@ -6791,6 +6793,11 @@ DEFPY(pim_msdp_peer, pim_msdp_peer_cmd, "./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); } @@ -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)) { @@ -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", "-"); } } @@ -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; @@ -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); @@ -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"); } } diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 5e5ee5e91f27..67bcab8eeda2 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -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) @@ -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; } @@ -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, diff --git a/pimd/pim_msdp.h b/pimd/pim_msdp.h index 4edb6e6166ec..33af2d0b6be8 100644 --- a/pimd/pim_msdp.h +++ b/pimd/pim_msdp.h @@ -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 { @@ -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, diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c index b55541b81026..ab002cceb3b3 100644 --- a/pimd/pim_nb.c +++ b/pimd/pim_nb.c @@ -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 = { diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h index a5ef6ad60a8f..660ece67fa93 100644 --- a/pimd/pim_nb.h +++ b/pimd/pim_nb.h @@ -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( diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index b55d08bab9c9..d6de39a81bbb 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -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); @@ -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 */ /* diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index 75e921382549..0581670392a3 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -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; +} diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h index 84d6b7f6c278..c1bada9d34f0 100644 --- a/pimd/pim_rpf.h +++ b/pimd/pim_rpf.h @@ -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 */ diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index febc595ad4c4..ef2cc3ff1b1a 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -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: "); diff --git a/pimd/pim_zlookup.h b/pimd/pim_zlookup.h index c9461eb7e3df..1bf7db60eede 100644 --- a/pimd/pim_zlookup.h +++ b/pimd/pim_zlookup.h @@ -35,4 +35,15 @@ int zclient_lookup_nexthop(struct pim_instance *pim, void pim_zlookup_show_ip_multicast(struct vty *vty); int pim_zlookup_sg_statistics(struct channel_oil *c_oil); + +/** + * Asks zebra to lookup for a route and return its information. + * + * \param pim PIM instance information. + * \param addr route address. + * \param type route type (e.g. if none specific then `ZEBRA_ROUTE_ALL`). + * \note call `zroute_info_free` after using the results. + */ +struct zroute_info *zclient_route_lookup(struct pim_instance *pim, const pim_addr *addr); + #endif /* PIM_ZLOOKUP_H */