Commit e35b5e4cb6 for frr
commit e35b5e4cb6e07f03aac36c210d107b64381ac6d4
Author: Carmine Scarpitta <cscarpit@cisco.com>
Date: Sun Jul 5 09:00:25 2026 +0200
bgpd: Export VPN routes as EVPN Type-5
EVPN Type-5 export only handled VRF unicast routes. VPN routes could not
be advertised as Type-5 routes.
Extend the Type-5 export path to handle VPN routes. Use the VPN RD 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 33bbc7adb9..d8b937a578 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -861,6 +861,13 @@ bool is_route_injectable_into_evpn_non_supp(struct bgp *bgp_vrf, afi_t afi, safi
IS_PATH_IMPORTED_FROM_EVPN_TABLE(pi))
return false;
+ if (safi == SAFI_MPLS_VPN) {
+ if (pi->peer != bgp_vrf->peer_self)
+ return false;
+ if (IS_PATH_IMPORTED_FROM_EVPN_TABLE(pi))
+ return false;
+ }
+
return true;
}
@@ -1946,8 +1953,15 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, struct bgp *bgp_v
/* Type-5 routes advertise the L3-VNI */
bgp_evpn_path_info_extra_get(pi);
pi->extra->evpn->type5_originator = originator;
- vni2label(bgp_vrf->l3vni, &bgp_labels.label[0]);
- bgp_labels.num_labels = 1;
+ if (originator && is_pi_family_vpn(originator)) {
+ if (BGP_PATH_INFO_NUM_LABELS(originator)) {
+ bgp_labels.label[0] = originator->extra->labels->label[0];
+ bgp_labels.num_labels = 1;
+ }
+ } else {
+ vni2label(bgp_vrf->l3vni, &bgp_labels.label[0]);
+ bgp_labels.num_labels = 1;
+ }
if (!bgp_path_info_labels_same(pi, &bgp_labels.label[0],
bgp_labels.num_labels)) {
bgp_labels_unintern(&pi->extra->labels);
@@ -2016,6 +2030,8 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct bgp_path_info *or
int route_changed = 0;
struct bgp_path_info *pi = NULL;
struct ipaddr vtep_ip;
+ struct prefix_rd *prd = &bgp_vrf->vrf_prd;
+ bgp_encap_types tnl_type = BGP_ENCAP_TYPE_VXLAN;
bgp_evpn = bgp_get_evpn();
if (!bgp_evpn)
@@ -2045,9 +2061,19 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct bgp_path_info *or
frrtrace(4, frr_bgp, evpn_advertise_type5, bgp_vrf->vrf_id, evp, &attr.rmac, &vtep_ip);
- if (src_afi == AFI_IP6 &&
- CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) {
+ if (src_safi == SAFI_MPLS_VPN) {
+ /* Set tnl_type to BGP_ENCAP_TYPE_RESERVED for VPN routes so
+ * build_evpn_type5_route_extcomm() does not add a Tunnel
+ * Encapsulation Extended Community.
+ *
+ * MPLS and SRv6 VPN routes do not need that extended community
+ * because the VPN label or SRv6 L3 service attribute already
+ * carries the forwarding information.
+ */
+ tnl_type = BGP_ENCAP_TYPE_RESERVED;
+ prd = (struct prefix_rd *)bgp_dest_get_prefix(originator->net->pdest);
+ } else if (src_afi == AFI_IP6 && CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) {
if (src_attr &&
!IN6_IS_ADDR_UNSPECIFIED(&src_attr->mp_nexthop_global)) {
struct bgp_route_evpn *bre =
@@ -2058,9 +2084,8 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct bgp_path_info *or
ipaddr_set_v6(&bre->gw_ip, &src_attr->mp_nexthop_global);
bgp_attr_set_evpn_overlay(&attr, bre);
}
- } else if (src_afi == AFI_IP &&
- CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) {
+ } else if (src_afi == AFI_IP && CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) {
if (src_attr && src_attr->nexthop.s_addr != 0) {
struct bgp_route_evpn *bre =
XCALLOC(MTYPE_BGP_EVPN_OVERLAY,
@@ -2073,11 +2098,10 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct bgp_path_info *or
}
/* Setup RT and encap extended community */
- build_evpn_type5_route_extcomm(bgp_vrf, &attr, BGP_ENCAP_TYPE_VXLAN);
+ build_evpn_type5_route_extcomm(bgp_vrf, &attr, tnl_type);
/* get the route node in global table */
- dest = bgp_evpn_global_node_get(bgp_evpn->rib[afi][safi], afi, safi,
- evp, &bgp_vrf->vrf_prd, NULL);
+ dest = bgp_evpn_global_node_get(bgp_evpn->rib[afi][safi], afi, safi, evp, prd, NULL);
assert(dest);
/* create or update the route entry within the route node */
@@ -2727,14 +2751,17 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, const struct bgp_path_in
struct bgp_dest *dest = NULL;
struct bgp_path_info *pi = NULL;
struct bgp *bgp_evpn = NULL; /* evpn bgp instance */
+ struct prefix_rd *prd = &bgp_vrf->vrf_prd;
bgp_evpn = bgp_get_evpn();
if (!bgp_evpn)
return 0;
+ if (originator && is_pi_family_vpn(originator))
+ prd = (struct prefix_rd *)bgp_dest_get_prefix(originator->net->pdest);
+
/* locate the global route entry for this type-5 prefix */
- dest = bgp_evpn_global_node_lookup(bgp_evpn->rib[afi][safi], safi, evp,
- &bgp_vrf->vrf_prd, NULL);
+ dest = bgp_evpn_global_node_lookup(bgp_evpn->rib[afi][safi], safi, evp, prd, NULL);
if (!dest)
return 0;
@@ -4858,6 +4885,12 @@ static void delete_withdraw_vrf_routes(struct bgp *bgp_vrf)
if (advertise_type5_routes_bestpath(bgp_vrf, AFI_IP6, SAFI_UNICAST) ||
advertise_type5_routes_multipath(bgp_vrf, AFI_IP6, SAFI_UNICAST))
bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST);
+
+ if (advertise_type5_routes_bestpath(bgp_vrf, AFI_IP, SAFI_MPLS_VPN))
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP, SAFI_MPLS_VPN);
+
+ if (advertise_type5_routes_bestpath(bgp_vrf, AFI_IP6, SAFI_MPLS_VPN))
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP6, SAFI_MPLS_VPN);
}
/*
@@ -4872,6 +4905,12 @@ void update_advertise_vrf_routes(struct bgp *bgp_vrf)
if (!bgp_evpn)
return;
+ if (advertise_type5_routes_bestpath(bgp_vrf, AFI_IP, SAFI_MPLS_VPN))
+ bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP, SAFI_MPLS_VPN);
+
+ if (advertise_type5_routes_bestpath(bgp_vrf, AFI_IP6, SAFI_MPLS_VPN))
+ bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP6, SAFI_MPLS_VPN);
+
if (!is_l3vni_live(bgp_vrf))
return; /* Nothing to do if no l3vni */
@@ -6176,6 +6215,35 @@ void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi, safi_t safi)
uint32_t addpath_id;
table = bgp_vrf->rib[afi][safi];
+ if (!table)
+ return;
+
+ if (safi == SAFI_MPLS_VPN) {
+ struct bgp_dest *pdest;
+
+ for (pdest = bgp_table_top(table); pdest; pdest = bgp_route_next(pdest)) {
+ struct bgp_table *rd_table;
+
+ /* This is the per-RD table of prefixes. */
+ rd_table = bgp_dest_get_bgp_table_info(pdest);
+ if (!rd_table)
+ continue;
+
+ for (dest = bgp_table_top(rd_table); dest; dest = bgp_route_next(dest)) {
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (!is_route_injectable_into_evpn(bgp_vrf, afi, safi, pi))
+ continue;
+
+ bgp_evpn_withdraw_type5_route(bgp_vrf, pi,
+ bgp_dest_get_prefix(dest),
+ afi, safi, 0);
+ break;
+ }
+ }
+ }
+ return;
+ }
+
for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
/* Use _non_supp variant: withdraw must not skip suppressed
* routes. A route may have been advertised while unsuppressed
@@ -6276,9 +6344,8 @@ void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct bgp_path_info *o
bgp_vrf->vrf_id, p);
}
-/* Inject all prefixes of a particular address-family (currently, IPv4 or
- * IPv6 unicast) into EVPN as type-5 routes. This is invoked when the
- * advertisement is enabled.
+/* Inject all IPv4/IPv6 prefixes into EVPN as type-5 routes.
+ * This is invoked when the advertisement is enabled.
*/
void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi,
safi_t safi)
@@ -6288,6 +6355,38 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi,
struct bgp_path_info *pi;
table = bgp_vrf->rib[afi][safi];
+ if (!table || !table->route_table)
+ return;
+
+ if (safi == SAFI_MPLS_VPN) {
+ struct bgp_dest *pdest;
+
+ for (pdest = bgp_table_top(table); pdest; pdest = bgp_route_next(pdest)) {
+ struct bgp_table *rd_table;
+
+ /* This is the per-RD table of prefixes. */
+ rd_table = bgp_dest_get_bgp_table_info(pdest);
+ if (!rd_table)
+ continue;
+
+ for (dest = bgp_table_top(rd_table); dest; dest = bgp_route_next(dest)) {
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (!is_route_injectable_into_evpn(bgp_vrf, afi, safi, pi))
+ continue;
+
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED) ||
+ (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) &&
+ !CHECK_FLAG(pi->flags, BGP_PATH_MULTIPATH)))
+ continue;
+
+ bgp_evpn_export_type5_route(bgp_vrf, dest, pi, afi, safi);
+ break;
+ }
+ }
+ }
+ return;
+ }
+
for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
/* Need to identify the "selected" route entry to use its
* attribute. Also, ensure that the route is injectable
@@ -7495,7 +7594,7 @@ int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi,
}
/*
- * Export IPv[46] unicast route from VRF to global table
+ * Export IPv[46] unicast or VPN route as EVPN type-5 route.
*/
void bgp_evpn_export_type5_route(struct bgp *bgp, struct bgp_dest *dest, struct bgp_path_info *pi,
afi_t afi, safi_t safi)
@@ -7515,7 +7614,7 @@ void bgp_evpn_export_type5_route(struct bgp *bgp, struct bgp_dest *dest, struct
*/
bgp_addpath_update_ids(bgp, dest, afi, safi);
- addpath_id = bgp_evpn_addpath_id_for_path(bgp, pi, afi);
+ addpath_id = safi == SAFI_MPLS_VPN ? 0 : bgp_evpn_addpath_id_for_path(bgp, pi, afi);
if (!bgp->adv_cmd_rmap[afi][safi].map) {
bgp_evpn_advertise_type5_route(bgp, pi, prefix, pi->attr, afi, safi, addpath_id);
return;
@@ -7537,7 +7636,7 @@ void bgp_evpn_export_type5_route(struct bgp *bgp, struct bgp_dest *dest, struct
}
/*
- * Unexport IPv[46] unicast route from VRF to global table
+ * Unexport IPv[46] unicast or VPN route from EVPN.
*/
void bgp_evpn_unexport_type5_route(struct bgp *bgp, const struct bgp_dest *dest,
const struct bgp_path_info *pi, afi_t afi, safi_t safi)
@@ -7545,7 +7644,7 @@ void bgp_evpn_unexport_type5_route(struct bgp *bgp, const struct bgp_dest *dest,
const struct prefix *prefix = bgp_dest_get_prefix(dest);
uint32_t addpath_id;
- addpath_id = bgp_evpn_addpath_id_for_path(bgp, pi, afi);
+ addpath_id = safi == SAFI_MPLS_VPN ? 0 : bgp_evpn_addpath_id_for_path(bgp, pi, afi);
bgp_evpn_withdraw_type5_route(bgp, pi, prefix, afi, safi, addpath_id);
}
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
index beab909d91..b453e8754d 100644
--- a/bgpd/bgp_evpn.h
+++ b/bgpd/bgp_evpn.h
@@ -25,6 +25,16 @@ static inline int advertise_type5_routes_bestpath(const struct bgp *bgp_vrf, afi
{
uint16_t flags = bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN];
+ if (safi == SAFI_MPLS_VPN) {
+ if (afi == AFI_IP && CHECK_FLAG(flags, BGP_L2VPN_EVPN_ADV_IPV4_VPN))
+ return 1;
+
+ if (afi == AFI_IP6 && CHECK_FLAG(flags, BGP_L2VPN_EVPN_ADV_IPV6_VPN))
+ return 1;
+
+ return 0;
+ }
+
if (!bgp_vrf->l3vni)
return 0;
@@ -41,6 +51,9 @@ static inline int advertise_type5_routes_multipath(const struct bgp *bgp_vrf, af
{
uint16_t flags = bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN];
+ if (safi == SAFI_MPLS_VPN)
+ return 0;
+
if (!bgp_vrf->l3vni)
return 0;
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index f3d3bdde09..205111daf4 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -4120,7 +4120,7 @@ static void bgp_process_evpn_route_injection(struct bgp *bgp, afi_t afi,
{
const struct prefix *p = bgp_dest_get_prefix(dest);
- if ((afi != AFI_IP && afi != AFI_IP6) || (safi != SAFI_UNICAST))
+ if ((afi != AFI_IP && afi != AFI_IP6) || (safi != SAFI_UNICAST && safi != SAFI_MPLS_VPN))
return;
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 1d1c4c0255..03a312515e 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -836,6 +836,9 @@ struct bgp {
#define BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL (1 << 11)
#define BGP_L2VPN_EVPN_SUPPRESS_IPV4_IMPORT_FROM_EVPN (1 << 12)
#define BGP_L2VPN_EVPN_SUPPRESS_IPV6_IMPORT_FROM_EVPN (1 << 13)
+/* EVPN VPN route advertisement flags */
+#define BGP_L2VPN_EVPN_ADV_IPV4_VPN (1 << 14)
+#define BGP_L2VPN_EVPN_ADV_IPV6_VPN (1 << 15)
/* BGP per AF peer count */
uint32_t af_peer_count[AFI_MAX][SAFI_MAX];