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 */