From 7452e879c353fabb5d6797bb241b023dcf1e801e Mon Sep 17 00:00:00 2001 From: vivek Date: Thu, 28 Feb 2019 08:19:21 +0000 Subject: [PATCH 1/8] bgpd: Leak EVPN-installed routes IPv4 or IPv6 unicast routes which are imported from EVPN routes (type-2 or type-5) and installed in a BGP instance can be leaked to another instance. Signed-off-by: Vivek Venkatraman Reviewed-by: Anuradha Karuppiah Reviewed-by: Donald Sharp --- bgpd/bgp_evpn.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 1471bd9829..1c65d69ef4 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -2508,6 +2508,9 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* Perform route selection and update zebra, if required. */ bgp_process(bgp_vrf, rn, afi, safi); + /* Process for route leaking. */ + vpn_leak_from_vrf_update(bgp_get_default(), bgp_vrf, pi); + return ret; } @@ -2673,6 +2676,9 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, if (!pi) return 0; + /* Process for route leaking. */ + vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp_vrf, pi); + bgp_aggregate_decrement(bgp_vrf, &rn->p, pi, afi, safi); /* Mark entry for deletion */ From 12d6100c52d14f7501cd04ffbb6a4f1b10ec3cd6 Mon Sep 17 00:00:00 2001 From: vivek Date: Thu, 28 Feb 2019 08:30:51 +0000 Subject: [PATCH 2/8] bgpd: Refine check for which routes can be exported into VPN A non-imported route or a non-VPN imported route is a candidate to be exported into the VPN routing table for leaking to other BGP instances or advertisement into BGP/MPLS VPN. The former is a local or learnt IPv4 or IPv6 route. The latter is an IPv4 or IPv6 route that is based on a received EVPN type-2 or type-5 route. Implement a function to specify if a route can be exported into VPN and use in the appropriate places. Signed-off-by: Vivek Venkatraman Reviewed-by: Anuradha Karuppiah Reviewed-by: Donald Sharp --- bgpd/bgp_mplsvpn.c | 9 ++++++--- bgpd/bgp_mplsvpn.h | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 4baac3e57a..461fef4387 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -683,11 +683,10 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ return; } - /* loop check - should not be an imported route. */ - if (path_vrf->extra && path_vrf->extra->bgp_orig) + /* Is this route exportable into the VPN table? */ + if (!is_route_injectable_into_vpn(path_vrf)) return; - if (!vpn_leak_to_vpn_active(bgp_vrf, afi, &debugmsg)) { if (debug) zlog_debug("%s: %s skipping: %s", __func__, @@ -912,6 +911,10 @@ void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn, /* to */ return; } + /* Is this route exportable into the VPN table? */ + if (!is_route_injectable_into_vpn(path_vrf)) + return; + if (!vpn_leak_to_vpn_active(bgp_vrf, afi, &debugmsg)) { if (debug) zlog_debug("%s: skipping: %s", __func__, debugmsg); diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 5b989e1853..c557b784af 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -226,6 +226,32 @@ static inline void vpn_leak_postchange(vpn_policy_direction_t direction, } } +/* Flag if the route is injectable into VPN. This would be either a + * non-imported route or a non-VPN imported route. + */ +static inline bool is_route_injectable_into_vpn(struct bgp_path_info *pi) +{ + struct bgp_path_info *parent_pi; + struct bgp_table *table; + struct bgp_node *rn; + + if (pi->sub_type != BGP_ROUTE_IMPORTED || + !pi->extra || + !pi->extra->parent) + return true; + + parent_pi = (struct bgp_path_info *)pi->extra->parent; + rn = parent_pi->net; + if (!rn) + return true; + table = bgp_node_table(rn); + if (table && + (table->afi == AFI_IP || table->afi == AFI_IP6) && + table->safi == SAFI_MPLS_VPN) + return false; + return true; +} + extern void vpn_policy_routemap_event(const char *rmap_name); extern vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey); From 0a2f9ac170f19a8739146838120a1518cd370029 Mon Sep 17 00:00:00 2001 From: vivek Date: Thu, 28 Feb 2019 11:11:01 +0000 Subject: [PATCH 3/8] bgpd: No nexthop tracking for EVPN-imported leaked routes IPv4 or IPv6 unicast routes which are imported from EVPN routes (type-2 or type-5) and installed in a BGP instance and then leaked do not need any nexthop tracking, as any tracking should happen in the source instance. Signed-off-by: Vivek Venkatraman Reviewed-by: Anuradha Karuppiah Reviewed-by: Donald Sharp --- bgpd/bgp_evpn.h | 6 ++++++ bgpd/bgp_mplsvpn.c | 14 +++++++++++--- bgpd/bgp_route.h | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 5c3d4ce3aa..c2fed76a0a 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -101,6 +101,12 @@ static inline int is_route_parent_evpn(struct bgp_path_info *ri) return 0; } +/* Flag if the route path's family is EVPN. */ +static inline bool is_pi_family_evpn(struct bgp_path_info *pi) +{ + return is_pi_family_matching(pi, AFI_L2VPN, SAFI_EVPN); +} + extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct prefix *p, struct attr *src_attr, afi_t afi, diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 461fef4387..1e2706f152 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -46,6 +46,7 @@ #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_nht.h" +#include "bgpd/bgp_evpn.h" #if ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -552,8 +553,12 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ if (bpi->extra && bpi->extra->bgp_orig) bgp_nexthop = bpi->extra->bgp_orig; - /* No nexthop tracking for redistributed routes */ - if (bpi_ultimate->sub_type == BGP_ROUTE_REDISTRIBUTE) + /* + * No nexthop tracking for redistributed routes or for + * EVPN-imported routes that get leaked. + */ + if (bpi_ultimate->sub_type == BGP_ROUTE_REDISTRIBUTE || + is_pi_family_evpn(bpi_ultimate)) nh_valid = 1; else /* @@ -614,8 +619,11 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ * No nexthop tracking for redistributed routes because * their originating protocols will do the tracking and * withdraw those routes if the nexthops become unreachable + * This also holds good for EVPN-imported routes that get + * leaked. */ - if (bpi_ultimate->sub_type == BGP_ROUTE_REDISTRIBUTE) + if (bpi_ultimate->sub_type == BGP_ROUTE_REDISTRIBUTE || + is_pi_family_evpn(bpi_ultimate)) nh_valid = 1; else /* diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 85325a93cf..1527571278 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -341,6 +341,24 @@ static inline int bgp_fibupd_safi(safi_t safi) return 0; } +/* Flag if the route path's family matches params. */ +static inline bool is_pi_family_matching(struct bgp_path_info *pi, + afi_t afi, safi_t safi) +{ + struct bgp_table *table; + struct bgp_node *rn; + + rn = pi->net; + if (!rn) + return false; + table = bgp_node_table(rn); + if (table && + table->afi == afi && + table->safi == safi) + return true; + return false; +} + /* Prototypes. */ extern void bgp_rib_remove(struct bgp_node *rn, struct bgp_path_info *pi, struct peer *peer, afi_t afi, safi_t safi); From abf386be5edbd3ca2ce13a2f275edc1625f0993a Mon Sep 17 00:00:00 2001 From: vivek Date: Thu, 28 Feb 2019 11:18:10 +0000 Subject: [PATCH 4/8] bgpd: Remove route sub-type checks in route-leak withdraw The check on which routes are exportable is a superset, so remove the route sub-type checks. Also, this change is needed to handle EVPN-imported leaked routes correctly. Signed-off-by: Vivek Venkatraman --- bgpd/bgp_mplsvpn.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 1e2706f152..39b0e2f1c8 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -901,15 +901,6 @@ void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn, /* to */ path_vrf->type, path_vrf->sub_type); } - if (path_vrf->sub_type != BGP_ROUTE_NORMAL - && path_vrf->sub_type != BGP_ROUTE_STATIC - && path_vrf->sub_type != BGP_ROUTE_REDISTRIBUTE) { - - if (debug) - zlog_debug("%s: wrong sub_type %d", __func__, - path_vrf->sub_type); - return; - } if (!bgp_vpn) return; From f106e3a72d37173b68f3f253d70a4af4ab7a7726 Mon Sep 17 00:00:00 2001 From: vivek Date: Thu, 28 Feb 2019 16:01:38 +0000 Subject: [PATCH 5/8] bgpd: Allow EVPN-sourced routes to be leaked back into EVPN Refine check on whether a route can be injected into EVPN to allow EVPN-sourced routes to be injected back into another instance. Signed-off-by: Vivek Venkatraman Reviewed-by: Anuradha Karuppiah Reviewed-by: Donald Sharp --- bgpd/bgp_evpn.c | 11 +++++++---- bgpd/bgp_evpn.h | 26 ++++++++++++++++++++++++++ bgpd/bgp_route.c | 10 ++++++---- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 1c65d69ef4..361df826fa 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -4234,11 +4234,13 @@ void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi, safi_t safi) table = bgp_vrf->rib[afi][safi]; for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - /* Only care about "selected" routes - non-imported. */ + /* Only care about "selected" routes. Also ensure that + * these are routes that are injectable into EVPN. + */ /* TODO: Support for AddPath for EVPN. */ for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) - && (!pi->extra || !pi->extra->parent)) { + && is_route_injectable_into_evpn(pi)) { bgp_evpn_withdraw_type5_route(bgp_vrf, &rn->p, afi, safi); break; @@ -4305,12 +4307,13 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi, table = bgp_vrf->rib[afi][safi]; for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { /* Need to identify the "selected" route entry to use its - * attribute. Also, we only consider "non-imported" routes. + * attribute. Also, ensure that the route is injectable + * into EVPN. * TODO: Support for AddPath for EVPN. */ for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) - && (!pi->extra || !pi->extra->parent)) { + && is_route_injectable_into_evpn(pi)) { /* apply the route-map */ if (bgp_vrf->adv_cmd_rmap[afi][safi].map) { diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index c2fed76a0a..0027ae51a4 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -107,6 +107,32 @@ static inline bool is_pi_family_evpn(struct bgp_path_info *pi) return is_pi_family_matching(pi, AFI_L2VPN, SAFI_EVPN); } +/* Flag if the route is injectable into EVPN. This would be either a + * non-imported route or a non-EVPN imported route. + */ +static inline bool is_route_injectable_into_evpn(struct bgp_path_info *pi) +{ + struct bgp_path_info *parent_pi; + struct bgp_table *table; + struct bgp_node *rn; + + if (pi->sub_type != BGP_ROUTE_IMPORTED || + !pi->extra || + !pi->extra->parent) + return true; + + parent_pi = (struct bgp_path_info *)pi->extra->parent; + rn = parent_pi->net; + if (!rn) + return true; + table = bgp_node_table(rn); + if (table && + table->afi == AFI_L2VPN && + table->safi == SAFI_EVPN) + return false; + return true; +} + extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct prefix *p, struct attr *src_attr, afi_t afi, diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 7ddc10ae01..0b6c536f5a 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2476,8 +2476,9 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, /* advertise/withdraw type-5 routes */ if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { - if (advertise_type5_routes(bgp, afi) && new_select && - (!new_select->extra || !new_select->extra->parent)) { + if (advertise_type5_routes(bgp, afi) && + new_select && + is_route_injectable_into_evpn(new_select)) { /* apply the route-map */ if (bgp->adv_cmd_rmap[afi][safi].map) { @@ -2500,8 +2501,9 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, afi, safi); } - } else if (advertise_type5_routes(bgp, afi) && old_select && - (!old_select->extra || !old_select->extra->parent)) + } else if (advertise_type5_routes(bgp, afi) && + old_select && + is_route_injectable_into_evpn(old_select)) bgp_evpn_withdraw_type5_route(bgp, &rn->p, afi, safi); } From 9544ddb28842e5e73e86880f3b12fa52bf264817 Mon Sep 17 00:00:00 2001 From: vivek Date: Fri, 1 Mar 2019 06:45:04 +0000 Subject: [PATCH 6/8] bgpd: Correctly identify VPN-imported routes in a VRF Refine check that looks for VPN routes imported into a VRF because a VRF can have other imported routes too like IPv4 and IPv6 unicast routes sourced from EVPN type-2 and type-5 routes. Signed-off-by: Vivek Venkatraman Reviewed-by: Anuradha Karuppiah Reviewed-by: Donald Sharp --- bgpd/bgp_mplsvpn.c | 5 ++++- bgpd/bgp_mplsvpn.h | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 39b0e2f1c8..2d3ff8b695 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1355,7 +1355,10 @@ void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, /* to */ for (bpi = bgp_node_get_bgp_path_info(bn); bpi; bpi = bpi->next) { - if (bpi->extra && bpi->extra->bgp_orig != bgp_vrf) { + if (bpi->extra + && bpi->extra->bgp_orig != bgp_vrf + && bpi->extra->parent + && is_pi_family_vpn(bpi->extra->parent)) { /* delete route */ bgp_aggregate_decrement(bgp_vrf, &bn->p, bpi, diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index c557b784af..2ef9570aac 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -252,6 +252,13 @@ static inline bool is_route_injectable_into_vpn(struct bgp_path_info *pi) return true; } +/* Flag if the route path's family is VPN. */ +static inline bool is_pi_family_vpn(struct bgp_path_info *pi) +{ + return (is_pi_family_matching(pi, AFI_IP, SAFI_MPLS_VPN) || + is_pi_family_matching(pi, AFI_IP6, SAFI_MPLS_VPN)); +} + extern void vpn_policy_routemap_event(const char *rmap_name); extern vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey); From 744c63be132a4644c7c3799a0f0d2a07bfc5b98a Mon Sep 17 00:00:00 2001 From: vivek Date: Fri, 1 Mar 2019 07:10:53 +0000 Subject: [PATCH 7/8] zebra: Use next hop's VRF for EVPN-based routes Ensure that the next hop's VRF is used for IPv4 and IPv6 unicast routes sourced from EVPN routes, for next hop and Router MAC tracking and install. This way, leaked routes from other instances are handled properly. Signed-off-by: Vivek Venkatraman Reviewed-by: Anuradha Karuppiah Reviewed-by: Donald Sharp --- zebra/zapi_msg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 9b91289dec..ef9917d4e6 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1463,8 +1463,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) &(api_nh->gate.ipv4), sizeof(struct in_addr)); zebra_vxlan_evpn_vrf_route_add( - vrf_id, &api_nh->rmac, &vtep_ip, - &api.prefix); + api_nh->vrf_id, &api_nh->rmac, + &vtep_ip, &api.prefix); } break; case NEXTHOP_TYPE_IPV6: @@ -1493,8 +1493,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) memcpy(&vtep_ip.ipaddr_v6, &(api_nh->gate.ipv6), sizeof(struct in6_addr)); zebra_vxlan_evpn_vrf_route_add( - vrf_id, &api_nh->rmac, &vtep_ip, - &api.prefix); + api_nh->vrf_id, &api_nh->rmac, + &vtep_ip, &api.prefix); } break; case NEXTHOP_TYPE_BLACKHOLE: From 08e68f925e274989c09249449c12f8870604ad5d Mon Sep 17 00:00:00 2001 From: vivek Date: Fri, 1 Mar 2019 07:17:16 +0000 Subject: [PATCH 8/8] bgpd: Recursively determine if route's source is EVPN With leaking of IPv4 or IPv6 unicast routes whose source is a EVPN type-2 or type-5 route between VRFs, the determination of whether the route's source is EVPN has to be made recursively. This is used during route install to pass along appropriate parameters to zebra. Signed-off-by: Vivek Venkatraman Reviewed-by: Anuradha Karuppiah Reviewed-by: Donald Sharp --- bgpd/bgp_evpn.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 0027ae51a4..dfe141c40e 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -88,8 +88,13 @@ static inline int is_route_parent_evpn(struct bgp_path_info *ri) !ri->extra->parent) return 0; - /* See if the parent is of family L2VPN/EVPN */ - parent_ri = (struct bgp_path_info *)ri->extra->parent; + /* Determine parent recursively */ + for (parent_ri = ri->extra->parent; + parent_ri->extra && parent_ri->extra->parent; + parent_ri = parent_ri->extra->parent) + ; + + /* See if of family L2VPN/EVPN */ rn = parent_ri->net; if (!rn) return 0;