Commit 89617f8f34 for frr

commit 89617f8f34e6983c4d16b1abdf340779603d7ede
Author: Carmine Scarpitta <cscarpit@cisco.com>
Date:   Sun Jul 5 13:22:04 2026 +0200

    bgpd: Import EVPN Type-5 routes through VPN RIB

    EVPN Type-5 import only handled routes through the EVPN import path.
    After VPN routes are advertised under the EVPN address family, bgpd also
    needs to import those Type-5 routes as VPN routes.

    Extend the Type-5 import path to handle VPN-backed routes. Install them
    in the VPN RIB and preserve the VPN forwarding information, including the
    MPLS label and the SRv6 L3 service attribute.

    Signed-off-by: Carmine Scarpitta <cscarpit@cisco.com>

diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index d8b937a578..05669e4135 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -3414,6 +3414,20 @@ static bool bgp_evpn_filter_ecommunity(uint8_t *val, uint8_t size, void *arg)
 	return true;
 }

+static bool bgp_evpn_filter_vpn_ecommunity(uint8_t *val, uint8_t size, void *arg)
+{
+	switch (val[0]) {
+	case ECOMMUNITY_ENCODE_OPAQUE:
+	case ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS:
+		if (val[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP)
+			return false;
+		break;
+	case ECOMMUNITY_ENCODE_EVPN:
+		return false;
+	}
+	return true;
+}
+
 /*
  * Install route entry into the VRF routing table and invoke route selection.
  */
@@ -4691,6 +4705,162 @@ static int install_uninstall_route_in_vnis(struct bgp *bgp, afi_t afi,
 	return 0;
 }

+static bool bgp_evpn_type5_route_has_tunnel_encap(const struct bgp_path_info *pi)
+{
+	struct ecommunity *ecom;
+	uint32_t i;
+
+	ecom = bgp_attr_get_ecommunity(pi->attr);
+	if (!ecom)
+		return false;
+
+	for (i = 0; i < ecom->size; i++) {
+		bgp_encap_types tnl_type;
+
+		if (ecommunity_tunnel_type(ecom, i, &tnl_type))
+			return true;
+	}
+
+	return false;
+}
+
+static bool bgp_evpn_type5_route_is_vpn(const struct prefix_evpn *evp,
+					const struct bgp_path_info *pi)
+{
+	if (evp->prefix.route_type != BGP_EVPN_IP_PREFIX_ROUTE)
+		return false;
+
+	if (!(is_evpn_prefix_ipaddr_v4(evp) || is_evpn_prefix_ipaddr_v6(evp)))
+		return false;
+
+	if (bgp_evpn_type5_route_has_tunnel_encap(pi))
+		return false;
+
+	return bgp_attr_get_srv6_l3service(pi->attr) || BGP_PATH_INFO_NUM_LABELS(pi);
+}
+
+static int install_uninstall_type5_route_in_vpn_rib(struct bgp *bgp, struct prefix_evpn *evp,
+						    struct bgp_path_info *pi, int install)
+{
+	struct attr attr;
+	struct attr *attr_new;
+	struct bgp *bgp_vpn = bgp_get_default();
+	struct bgp_dest *dest BGP_DEST_AUTOUNLOCK = NULL;
+	struct bgp_path_info *vpn_pi;
+	struct ecommunity *ecom;
+	struct prefix_rd *prd;
+	struct prefix p = {};
+	const mpls_label_t *labels;
+	uint8_t num_labels;
+	afi_t afi;
+
+	if (!bgp_evpn_type5_route_is_vpn(evp, pi))
+		return 0;
+
+	if (bgp_evpn_is_path_local(bgp, pi))
+		return 0;
+
+	if (!pi->net || !pi->net->pdest)
+		return -1;
+
+	ip_prefix_from_evpn_prefix(evp, &p);
+	if (is_evpn_prefix_ipaddr_v4(evp))
+		afi = AFI_IP;
+	else
+		afi = AFI_IP6;
+
+	if (!bgp_vpn->rib[afi][SAFI_MPLS_VPN])
+		return -1;
+
+	if (install &&
+	    !CHECK_FLAG(bgp_vpn->af_flags[afi][SAFI_MPLS_VPN], BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL) &&
+	    vpn_leak_to_vrf_no_retain_filter_check(bgp_vpn, pi->attr, afi))
+		return 0;
+
+	prd = (struct prefix_rd *)bgp_dest_get_prefix(pi->net->pdest);
+	if (install) {
+		dest = bgp_afi_node_get(bgp_vpn->rib[afi][SAFI_MPLS_VPN], afi, SAFI_MPLS_VPN, &p,
+					prd);
+		if (!dest)
+			return -1;
+	} else {
+		dest = bgp_safi_node_lookup(bgp_vpn->rib[afi][SAFI_MPLS_VPN], SAFI_MPLS_VPN, &p,
+					    prd);
+		if (!dest)
+			return 0;
+	}
+
+	for (vpn_pi = bgp_dest_get_bgp_path_info(dest); vpn_pi; vpn_pi = vpn_pi->next)
+		if (vpn_pi->extra && vpn_pi->extra->evpn &&
+		    vpn_pi->extra->evpn->type5_originator == pi)
+			break;
+
+	if (!install) {
+		if (vpn_pi) {
+			vpn_leak_to_vrf_withdraw(vpn_pi);
+			bgp_rib_remove(dest, vpn_pi, vpn_pi->peer, afi, SAFI_MPLS_VPN);
+		}
+		return 0;
+	}
+
+	num_labels = BGP_PATH_INFO_NUM_LABELS(pi);
+	labels = num_labels ? pi->extra->labels->label : NULL;
+
+	bgp_attr_dup_into(&attr, pi->attr);
+	ecom = ecommunity_filter(bgp_attr_get_ecommunity(&attr), bgp_evpn_filter_vpn_ecommunity,
+				 NULL);
+	bgp_attr_set_ecommunity(&attr, ecom);
+	attr.encap_tunneltype = BGP_ENCAP_TYPE_RESERVED;
+	memset(&attr.rmac, 0, sizeof(attr.rmac));
+
+	if (!vpn_pi) {
+		attr_new = bgp_attr_intern(&attr);
+		vpn_pi = info_make(pi->type, BGP_ROUTE_IMPORTED, 0, pi->peer, attr_new, dest);
+		SET_FLAG(vpn_pi->flags, BGP_PATH_VALID);
+		bgp_evpn_path_info_extra_get(vpn_pi);
+		vpn_pi->extra->evpn->type5_originator = pi;
+
+		if (num_labels)
+			vpn_pi->extra->labels = bgp_labels_intern(pi->extra->labels);
+
+		bgp_path_info_add(dest, vpn_pi);
+	} else {
+		if (!CHECK_FLAG(vpn_pi->flags, BGP_PATH_REMOVED) &&
+		    attrhash_cmp(vpn_pi->attr, &attr) &&
+		    bgp_path_info_labels_same(vpn_pi, labels, num_labels)) {
+			bgp_attr_extra_discard(&attr);
+			return 0;
+		}
+
+		attr_new = bgp_attr_intern(&attr);
+
+		if (CHECK_FLAG(vpn_pi->flags, BGP_PATH_REMOVED))
+			bgp_path_info_restore(dest, vpn_pi);
+		else
+			bgp_aggregate_decrement(bgp_vpn, bgp_dest_get_prefix(dest), vpn_pi, afi,
+						SAFI_MPLS_VPN);
+
+		bgp_path_info_set_flag(dest, vpn_pi, BGP_PATH_ATTR_CHANGED);
+		bgp_attr_unintern(&vpn_pi->attr);
+		vpn_pi->attr = attr_new;
+		vpn_pi->uptime = monotime(NULL);
+
+		if (!bgp_path_info_labels_same(vpn_pi, labels, num_labels)) {
+			bgp_path_info_extra_get(vpn_pi);
+			bgp_labels_unintern(&vpn_pi->extra->labels);
+			if (num_labels)
+				vpn_pi->extra->labels = bgp_labels_intern(pi->extra->labels);
+		}
+	}
+
+	bgp_aggregate_increment(bgp_vpn, bgp_dest_get_prefix(dest), vpn_pi, afi, SAFI_MPLS_VPN);
+	bgp_process(bgp_vpn, dest, vpn_pi, afi, SAFI_MPLS_VPN);
+	vpn_leak_to_vrf_update(bgp_vpn, vpn_pi, prd, vpn_pi->peer);
+	bgp_attr_flush(&attr);
+
+	return 0;
+}
+
 /*
  * Install or uninstall route for appropriate VNIs/ESIs.
  */
@@ -4734,6 +4904,9 @@ static int bgp_evpn_install_uninstall_table(struct bgp *bgp, afi_t afi, safi_t s
 	if (import && bgp_evpn_route_matches_macvrf_soo(pi, evp))
 		return 0;

+	if (bgp_evpn_type5_route_is_vpn(evp, pi))
+		return install_uninstall_type5_route_in_vpn_rib(bgp, evp, pi, import);
+
 	/* An EVPN route belongs to a VNI or a VRF or an ESI based on the RTs
 	 * attached to the route */
 	for (i = 0; i < ecom->size; i++) {