diff options
75 files changed, 3950 insertions, 189 deletions
diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c index 1df646c34..0e341a8c6 100644 --- a/bgpd/bgp_attr_evpn.c +++ b/bgpd/bgp_attr_evpn.c @@ -315,3 +315,4 @@ extern bool is_zero_gw_ip(const union gw_addr *gw_ip, const afi_t afi) return false; } + diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h index 6fdf73fd1..102509fdd 100644 --- a/bgpd/bgp_attr_evpn.h +++ b/bgpd/bgp_attr_evpn.h @@ -30,7 +30,21 @@ union gw_addr { struct in6_addr ipv6; }; +enum overlay_index_type { + OVERLAY_INDEX_TYPE_NONE, + OVERLAY_INDEX_GATEWAY_IP, + OVERLAY_INDEX_ESI, + OVERLAY_INDEX_MAC, +}; + +/* + * Structure to store ovrelay index for EVPN type-5 route + * This structure stores ESI and Gateway IP overlay index. + * MAC overlay index is stored in the RMAC attribute. + */ struct bgp_route_evpn { + enum overlay_index_type type; + esi_t eth_s_id; union gw_addr gw_ip; }; diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 8f286e66d..856afb05f 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -2680,10 +2680,14 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels, int addpath_valid, uint32_t addpath_id, + struct bgp_route_evpn *overlay_index, char *str, int size) { char rd_buf[RD_ADDRSTRLEN]; char tag_buf[30]; + char overlay_index_buf[INET6_ADDRSTRLEN + 14]; + const struct prefix_evpn *evp; + /* ' with addpath ID ' 17 * max strlen of uint32 + 10 * +/- (just in case) + 1 @@ -2701,6 +2705,23 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, snprintf(pathid_buf, sizeof(pathid_buf), " with addpath ID %u", addpath_id); + overlay_index_buf[0] = '\0'; + if (overlay_index && overlay_index->type == OVERLAY_INDEX_GATEWAY_IP) { + char obuf[INET6_ADDRSTRLEN]; + + obuf[0] = '\0'; + evp = pu.evp; + if (is_evpn_prefix_ipaddr_v4(evp)) + inet_ntop(AF_INET, &overlay_index->gw_ip, obuf, + sizeof(obuf)); + else if (is_evpn_prefix_ipaddr_v6(evp)) + inet_ntop(AF_INET6, &overlay_index->gw_ip, obuf, + sizeof(obuf)); + + snprintf(overlay_index_buf, sizeof(overlay_index_buf), + " gateway IP %s", obuf); + } + tag_buf[0] = '\0'; if (bgp_labeled_safi(safi) && num_labels) { @@ -2720,9 +2741,10 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, } if (prd) - snprintfrr(str, size, "RD %s %pFX%s%s %s %s", + snprintfrr(str, size, "RD %s %pFX%s%s%s %s %s", prefix_rd2str(prd, rd_buf, sizeof(rd_buf)), pu.p, - tag_buf, pathid_buf, afi2str(afi), safi2str(safi)); + overlay_index_buf, tag_buf, pathid_buf, afi2str(afi), + safi2str(safi)); else if (safi == SAFI_FLOWSPEC) { char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX]; const struct prefix_fs *fs = pu.fs; diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index fa8da1c34..d847fb84e 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -37,7 +37,8 @@ #define DUMP_DETAIL 32 /* RD + Prefix + Path-Id */ -#define BGP_PRD_PATH_STRLEN (PREFIX_STRLEN + RD_ADDRSTRLEN + 20) +#define BGP_PRD_PATH_STRLEN \ + (PREFIX_STRLEN + RD_ADDRSTRLEN + INET6_ADDRSTRLEN + 34) extern int dump_open; extern int dump_update; @@ -179,11 +180,11 @@ extern bool bgp_debug_update(struct peer *peer, const struct prefix *p, extern bool bgp_debug_bestpath(struct bgp_dest *dest); extern bool bgp_debug_zebra(const struct prefix *p); -extern const char * -bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, const struct prefix_rd *prd, - union prefixconstptr pu, mpls_label_t *label, - uint32_t num_labels, int addpath_valid, - uint32_t addpath_id, char *str, int size); +extern const char *bgp_debug_rdpfxpath2str( + afi_t afi, safi_t safi, const struct prefix_rd *prd, + union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels, + int addpath_valid, uint32_t addpath_id, + struct bgp_route_evpn *overlay_index, char *str, int size); const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data, size_t datalen); diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index d8e57419e..e08e5b979 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -53,6 +53,7 @@ #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_mac.h" #include "bgpd/bgp_vty.h" +#include "bgpd/bgp_nht.h" /* * Definitions and external declarations. @@ -65,6 +66,28 @@ DEFINE_QOBJ_TYPE(bgp_evpn_es); * Static function declarations */ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn); +static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *evpn); +static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *evpn); +static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn, + struct bgp_path_info *pi); +static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn, + struct bgp_path_info *pi); +static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn, + void (*func)(struct hash_bucket *, + void *), + void *arg); +static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn); +static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp, + struct bgpevpn *vpn); +static unsigned int vni_svi_hash_key_make(const void *p); +static bool vni_svi_hash_cmp(const void *p1, const void *p2); +static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn, + struct ipaddr *addr, + bool resolve); +static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket, + void *args); +static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket, + void *args); static struct in_addr zero_vtep_ip; /* @@ -1261,7 +1284,8 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, /* update evpn type-5 route entry */ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, - struct attr *src_attr) + struct attr *src_attr, afi_t src_afi, + safi_t src_safi) { afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; @@ -1315,6 +1339,26 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + 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)) { + attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP; + memcpy(&attr.evpn_overlay.gw_ip.ipv6, + &src_attr->mp_nexthop_global, + sizeof(struct in6_addr)); + } + } 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) { + attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP; + memcpy(&attr.evpn_overlay.gw_ip.ipv4, + &src_attr->nexthop, sizeof(struct in_addr)); + } + } + /* Setup RT and encap extended community */ build_evpn_type5_route_extcomm(bgp_vrf, &attr); @@ -2198,6 +2242,7 @@ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) dest = bgp_route_next(dest)) { for (pi = bgp_dest_get_bgp_path_info(dest); (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { + bgp_evpn_remote_ip_hash_del(vpn, pi); bgp_path_info_delete(dest, pi); bgp_path_info_reap(dest, pi); } @@ -2381,6 +2426,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, bool new_pi = false; bool use_l3nhg = false; bool is_l3nhg_active = false; + char buf1[INET6_ADDRSTRLEN]; memset(pp, 0, sizeof(struct prefix)); ip_prefix_from_evpn_prefix(evp, pp); @@ -2411,10 +2457,36 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, * make sure to set the flag for next hop attribute. */ attr = *parent_pi->attr; - if (afi == AFI_IP6) - evpn_convert_nexthop_to_ipv6(&attr); - else - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + if (attr.evpn_overlay.type != OVERLAY_INDEX_GATEWAY_IP) { + if (afi == AFI_IP6) + evpn_convert_nexthop_to_ipv6(&attr); + else + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + } else { + + /* + * If gateway IP overlay index is specified in the NLRI of + * EVPN RT-5, this gateway IP should be used as the nexthop + * for the prefix in the VRF + */ + if (bgp_debug_zebra(NULL)) { + zlog_debug( + "Install gateway IP %s as nexthop for prefix %pFX in vrf %s", + inet_ntop(pp->family, &attr.evpn_overlay.gw_ip, + buf1, sizeof(buf1)), pp, + vrf_id_to_name(bgp_vrf->vrf_id)); + } + + if (afi == AFI_IP6) { + memcpy(&attr.mp_nexthop_global, + &attr.evpn_overlay.gw_ip.ipv6, + sizeof(struct in6_addr)); + attr.mp_nexthop_len = IPV6_MAX_BYTELEN; + } else { + attr.nexthop = attr.evpn_overlay.gw_ip.ipv4; + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + } + } bgp_evpn_es_vrf_use_nhg(bgp_vrf, &parent_pi->attr->esi, &use_l3nhg, &is_l3nhg_active, NULL); @@ -2460,8 +2532,27 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, pi->attr = attr_new; pi->uptime = bgp_clock(); } - /* as it is an importation, change nexthop */ - bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF); + + /* Gateway IP nexthop should be resolved */ + if (attr.evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) { + if (bgp_find_or_add_nexthop(bgp_vrf, bgp_vrf, afi, safi, pi, + NULL, 0)) + bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); + else { + if (BGP_DEBUG(nht, NHT)) { + inet_ntop(pp->family, + &attr.evpn_overlay.gw_ip, + buf1, sizeof(buf1)); + zlog_debug("%s: gateway IP NH unresolved", + buf1); + } + bgp_path_info_unset_flag(dest, pi, BGP_PATH_VALID); + } + } else { + + /* as it is an importation, change nexthop */ + bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF); + } /* Link path to evpn nexthop */ bgp_evpn_path_nh_add(bgp_vrf, pi); @@ -2565,6 +2656,9 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, pi->uptime = bgp_clock(); } + /* Add this route to remote IP hashtable */ + bgp_evpn_remote_ip_hash_add(vpn, pi); + /* Perform route selection and update zebra, if required. */ ret = evpn_route_select_install(bgp, vpn, dest); @@ -2693,6 +2787,8 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, if (!pi) return 0; + bgp_evpn_remote_ip_hash_del(vpn, pi); + /* Mark entry for deletion */ bgp_path_info_delete(dest, pi); @@ -3951,7 +4047,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, mpls_label_t label; /* holds the VNI as in the packet */ int ret; afi_t gw_afi; - bool is_valid_update = false; + bool is_valid_update = true; /* Type-5 route should be 34 or 58 bytes: * RD (8), ESI (10), Eth Tag (4), IP len (1), IP (4 or 16), @@ -3980,9 +4076,9 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, /* Additional information outside of prefix - ESI and GW IP */ memset(&evpn, 0, sizeof(evpn)); - /* Fetch ESI */ + /* Fetch ESI overlay index */ if (attr) - memcpy(&attr->esi, pfx, sizeof(esi_t)); + memcpy(&evpn.eth_s_id, pfx, sizeof(esi_t)); pfx += ESI_BYTES; /* Fetch Ethernet Tag. */ @@ -4031,25 +4127,53 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, * field */ + /* + * An update containing a non-zero gateway IP and a non-zero ESI + * at the same time is should be treated as withdraw + */ + if (bgp_evpn_is_esi_valid(&evpn.eth_s_id) + && !is_zero_gw_ip(&evpn.gw_ip, gw_afi)) { + flog_err(EC_BGP_EVPN_ROUTE_INVALID, + "%s - Rx EVPN Type-5 ESI and gateway-IP both non-zero.", + peer->host); + is_valid_update = false; + } else if (bgp_evpn_is_esi_valid(&evpn.eth_s_id)) + evpn.type = OVERLAY_INDEX_ESI; + else if (!is_zero_gw_ip(&evpn.gw_ip, gw_afi)) + evpn.type = OVERLAY_INDEX_GATEWAY_IP; if (attr) { - is_valid_update = true; - if (is_zero_mac(&attr->rmac) && - is_zero_gw_ip(&evpn.gw_ip, gw_afi)) + if (is_zero_mac(&attr->rmac) + && !bgp_evpn_is_esi_valid(&evpn.eth_s_id) + && is_zero_gw_ip(&evpn.gw_ip, gw_afi) && label == 0) { + flog_err(EC_BGP_EVPN_ROUTE_INVALID, + "%s - Rx EVPN Type-5 ESI, gateway-IP, RMAC and label all zero", + peer->host); is_valid_update = false; + } if (is_mcast_mac(&attr->rmac) || is_bcast_mac(&attr->rmac)) is_valid_update = false; } /* Process the route. */ - if (is_valid_update) + if (attr && is_valid_update) ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label, 1, 0, &evpn); - else + else { + if (!is_valid_update) { + char attr_str[BUFSIZ] = {0}; + + bgp_dump_attr(attr, attr_str, BUFSIZ); + zlog_warn( + "Invalid update from peer %s vrf %u prefix %pFX attr %s - treat as withdraw", + peer->hostname, peer->bgp->vrf_id, &p, + attr_str); + } ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label, 1, &evpn); + } return ret; } @@ -4078,7 +4202,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p, /* Prefix contains RD, ESI, EthTag, IP length, IP, GWIP and VNI */ stream_putc(s, 8 + 10 + 4 + 1 + len + 3); stream_put(s, prd->val, 8); - if (attr) + if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_ESI) stream_put(s, &attr->esi, sizeof(esi_t)); else stream_put(s, 0, sizeof(esi_t)); @@ -4088,7 +4212,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p, stream_put_ipv4(s, p_evpn_p->prefix_addr.ip.ipaddr_v4.s_addr); else stream_put(s, &p_evpn_p->prefix_addr.ip.ipaddr_v6, 16); - if (attr) { + if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) { const struct bgp_route_evpn *evpn_overlay = bgp_attr_get_evpn_overlay(attr); @@ -4301,7 +4425,7 @@ void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, const struct prefix *p, struct prefix_evpn evp; build_type5_prefix_from_ip_prefix(&evp, p); - ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr); + ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr, afi, safi); if (ret) flog_err(EC_BGP_EVPN_ROUTE_CREATE, "%u: Failed to create type-5 route for prefix %pFX", @@ -5134,7 +5258,8 @@ struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni) struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp) + struct in_addr mcast_grp, + ifindex_t svi_ifindex) { struct bgpevpn *vpn; @@ -5148,6 +5273,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, vpn->originator_ip = originator_ip; vpn->tenant_vrf_id = tenant_vrf_id; vpn->mcast_grp = mcast_grp; + vpn->svi_ifindex = svi_ifindex; /* Initialize route-target import and export lists */ vpn->import_rtl = list_new(); @@ -5168,6 +5294,9 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, return NULL; } + bgp_evpn_remote_ip_hash_init(vpn); + bgp_evpn_link_to_vni_svi_hash(bgp, vpn); + /* add to l2vni list on corresponding vrf */ bgpevpn_link_to_l3vni(vpn); @@ -5185,6 +5314,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, */ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) { + bgp_evpn_remote_ip_hash_destroy(vpn); bgp_evpn_vni_es_cleanup(vpn); bgpevpn_unlink_from_l3vni(vpn); bgp_table_unlock(vpn->route_table); @@ -5192,6 +5322,7 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) list_delete(&vpn->import_rtl); list_delete(&vpn->export_rtl); bf_release_index(bm->rd_idspace, vpn->rd_id); + hash_release(bgp->vni_svi_hash, vpn); hash_release(bgp->vnihash, vpn); QOBJ_UNREG(vpn); XFREE(MTYPE_BGP_EVPN, vpn); @@ -5603,6 +5734,9 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) */ delete_routes_for_vni(bgp, vpn); + bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn); + + vpn->svi_ifindex = 0; /* * tunnel is no longer active, del tunnel ip address from tip_hash */ @@ -5623,8 +5757,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp) - + struct in_addr mcast_grp, + ifindex_t svi_ifindex) { struct bgpevpn *vpn; struct prefix_evpn p; @@ -5636,18 +5770,65 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, if (is_vni_live(vpn) && IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip) && IPV4_ADDR_SAME(&vpn->mcast_grp, &mcast_grp) - && vpn->tenant_vrf_id == tenant_vrf_id) + && vpn->tenant_vrf_id == tenant_vrf_id + && vpn->svi_ifindex == svi_ifindex) /* Probably some other param has changed that we don't * care about. */ return 0; bgp_evpn_mcast_grp_change(bgp, vpn, mcast_grp); + if (vpn->svi_ifindex != svi_ifindex) { + + /* + * Unresolve all the gateway IP nexthops for this VNI + * for old SVI + */ + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_unlink_nexthop, + vpn); + bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn); + vpn->svi_ifindex = svi_ifindex; + bgp_evpn_link_to_vni_svi_hash(bgp, vpn); + + /* + * Resolve all the gateway IP nexthops for this VNI + * for new SVI + */ + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_link_nexthop, + vpn); + } + /* Update tenant_vrf_id if it has changed. */ if (vpn->tenant_vrf_id != tenant_vrf_id) { + + /* + * Unresolve all the gateway IP nexthops for this VNI + * in old tenant vrf + */ + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_unlink_nexthop, + vpn); bgpevpn_unlink_from_l3vni(vpn); vpn->tenant_vrf_id = tenant_vrf_id; bgpevpn_link_to_l3vni(vpn); + + /* + * Resolve all the gateway IP nexthops for this VNI + * in new tenant vrf + */ + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_link_nexthop, + vpn); } /* If tunnel endpoint IP has changed, update (and delete prior @@ -5666,7 +5847,7 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, /* Create or update as appropriate. */ if (!vpn) { vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id, - mcast_grp); + mcast_grp, svi_ifindex); if (!vpn) { flog_err( EC_BGP_VNI, @@ -5768,6 +5949,8 @@ void bgp_evpn_cleanup(struct bgp *bgp) hash_free(bgp->vrf_import_rt_hash); bgp->vrf_import_rt_hash = NULL; + hash_free(bgp->vni_svi_hash); + bgp->vni_svi_hash = NULL; hash_free(bgp->vnihash); bgp->vnihash = NULL; @@ -5786,6 +5969,9 @@ void bgp_evpn_init(struct bgp *bgp) { bgp->vnihash = hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash"); + bgp->vni_svi_hash = + hash_create(vni_svi_hash_key_make, vni_svi_hash_cmp, + "BGP VNI hash based on SVI ifindex"); bgp->import_rt_hash = hash_create(import_rt_hash_key_make, import_rt_hash_cmp, "BGP Import RT Hash"); @@ -5878,3 +6064,408 @@ bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx) return false; } + +static void *bgp_evpn_remote_ip_hash_alloc(void *p) +{ + const struct evpn_remote_ip *key = (const struct evpn_remote_ip *)p; + struct evpn_remote_ip *ip; + + ip = XMALLOC(MTYPE_EVPN_REMOTE_IP, sizeof(struct evpn_remote_ip)); + *ip = *key; + ip->macip_path_list = list_new(); + + return ip; +} + +static unsigned int bgp_evpn_remote_ip_hash_key_make(const void *p) +{ + const struct evpn_remote_ip *ip = p; + const struct ipaddr *addr = &ip->addr; + + if (IS_IPADDR_V4(addr)) + return jhash_1word(addr->ipaddr_v4.s_addr, 0); + + return jhash2(addr->ipaddr_v6.s6_addr32, + array_size(addr->ipaddr_v6.s6_addr32), 0); +} + +static bool bgp_evpn_remote_ip_hash_cmp(const void *p1, const void *p2) +{ + const struct evpn_remote_ip *ip1 = p1; + const struct evpn_remote_ip *ip2 = p2; + + return (memcmp(&ip1->addr, &ip2->addr, sizeof(struct ipaddr)) == 0); +} + +static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *vpn) +{ + if (!evpn_resolve_overlay_index()) + return; + + vpn->remote_ip_hash = hash_create(bgp_evpn_remote_ip_hash_key_make, + bgp_evpn_remote_ip_hash_cmp, + "BGP EVPN remote IP hash"); +} + +static void bgp_evpn_remote_ip_hash_free(struct hash_bucket *bucket, void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); + + list_delete(&ip->macip_path_list); + + hash_release(vpn->remote_ip_hash, ip); + XFREE(MTYPE_EVPN_REMOTE_IP, ip); +} + +static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *vpn) +{ + if (!evpn_resolve_overlay_index() || vpn->remote_ip_hash == NULL) + return; + + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, void *))bgp_evpn_remote_ip_hash_free, + vpn); + + hash_free(vpn->remote_ip_hash); + vpn->remote_ip_hash = NULL; +} + +/* Add a remote MAC/IP route to hash table */ +static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn, + struct bgp_path_info *pi) +{ + struct evpn_remote_ip tmp; + struct evpn_remote_ip *ip; + struct prefix_evpn *evp; + + if (!evpn_resolve_overlay_index()) + return; + + if (pi->type != ZEBRA_ROUTE_BGP || pi->sub_type != BGP_ROUTE_IMPORTED + || !CHECK_FLAG(pi->flags, BGP_PATH_VALID)) + return; + + evp = (struct prefix_evpn *)&pi->net->p; + + if (evp->family != AF_EVPN + || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE + || is_evpn_prefix_ipaddr_none(evp)) + return; + + tmp.addr = evp->prefix.macip_addr.ip; + ip = hash_lookup(vpn->remote_ip_hash, &tmp); + if (ip) { + if (listnode_lookup(ip->macip_path_list, pi) != NULL) + return; + (void)listnode_add(ip->macip_path_list, pi); + return; + } + + ip = hash_get(vpn->remote_ip_hash, &tmp, bgp_evpn_remote_ip_hash_alloc); + if (!ip) + return; + + (void)listnode_add(ip->macip_path_list, pi); + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true); +} + +/* Delete a remote MAC/IP route from hash table */ +static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn, + struct bgp_path_info *pi) +{ + struct evpn_remote_ip tmp; + struct evpn_remote_ip *ip; + struct prefix_evpn *evp; + + if (!evpn_resolve_overlay_index()) + return; + + evp = (struct prefix_evpn *)&pi->net->p; + + if (evp->family != AF_EVPN + || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE + || is_evpn_prefix_ipaddr_none(evp)) + return; + + tmp.addr = evp->prefix.macip_addr.ip; + ip = hash_lookup(vpn->remote_ip_hash, &tmp); + if (ip == NULL) + return; + + listnode_delete(ip->macip_path_list, pi); + + if (ip->macip_path_list->count == 0) { + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); + hash_release(vpn->remote_ip_hash, ip); + XFREE(MTYPE_EVPN_REMOTE_IP, ip); + } +} + +static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn, + void (*func)(struct hash_bucket *, + void *), + void *arg) +{ + if (!evpn_resolve_overlay_index()) + return; + + hash_iterate(vpn->remote_ip_hash, func, arg); +} + +static void show_remote_ip_entry(struct hash_bucket *bucket, void *args) +{ + char buf[INET6_ADDRSTRLEN]; + char buf2[EVPN_ROUTE_STRLEN]; + struct prefix_evpn *evp; + + struct listnode *node = NULL; + struct bgp_path_info *pi = NULL; + struct vty *vty = (struct vty *)args; + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + + vty_out(vty, " Remote IP: %s\n", + ipaddr2str(&ip->addr, buf, sizeof(buf))); + vty_out(vty, " Linked MAC/IP routes:\n"); + for (ALL_LIST_ELEMENTS_RO(ip->macip_path_list, node, pi)) { + evp = (struct prefix_evpn *)&pi->net->p; + prefix2str(evp, buf2, sizeof(buf2)); + vty_out(vty, " %s\n", buf2); + } +} + +void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, void *args) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + struct vty *vty = (struct vty *)args; + + vty_out(vty, "VNI: %u\n", vpn->vni); + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *))show_remote_ip_entry, + vty); + vty_out(vty, "\n"); +} + +static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket, + void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true); +} + +static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket, + void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); +} + +static unsigned int vni_svi_hash_key_make(const void *p) +{ + const struct bgpevpn *vpn = p; + + return jhash_1word(vpn->svi_ifindex, 0); +} + +static bool vni_svi_hash_cmp(const void *p1, const void *p2) +{ + const struct bgpevpn *vpn1 = p1; + const struct bgpevpn *vpn2 = p2; + + return (vpn1->svi_ifindex == vpn2->svi_ifindex); +} + +static struct bgpevpn *bgp_evpn_vni_svi_hash_lookup(struct bgp *bgp, + ifindex_t svi) +{ + struct bgpevpn *vpn; + struct bgpevpn tmp; + + memset(&tmp, 0, sizeof(struct bgpevpn)); + tmp.svi_ifindex = svi; + vpn = hash_lookup(bgp->vni_svi_hash, &tmp); + return vpn; +} + +static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn) +{ + if (vpn->svi_ifindex == 0) + return; + + hash_get(bgp->vni_svi_hash, vpn, hash_alloc_intern); +} + +static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp, + struct bgpevpn *vpn) +{ + if (vpn->svi_ifindex == 0) + return; + + hash_release(bgp->vni_svi_hash, vpn); +} + +void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args) +{ + struct bgpevpn *evpn = (struct bgpevpn *)bucket->data; + struct vty *vty = (struct vty *)args; + + vty_out(vty, "SVI: %u VNI: %u\n", evpn->svi_ifindex, evpn->vni); +} + +/* + * This function is called for a bgp_nexthop_cache entry when the nexthop is + * gateway IP overlay index. + * This function returns true if there is a remote MAC/IP route for the gateway + * IP in the EVI of the nexthop SVI. + */ +bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc) +{ + struct bgp *bgp_evpn = NULL; + struct bgpevpn *vpn = NULL; + struct evpn_remote_ip tmp; + struct prefix *p; + + if (!evpn_resolve_overlay_index()) + return false; + + if (!bnc->nexthop || bnc->nexthop->ifindex == 0) + return false; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return false; + + /* + * Gateway IP is resolved by nht over SVI interface. + * Use this SVI to find corresponding EVI(L2 context) + */ + vpn = bgp_evpn_vni_svi_hash_lookup(bgp_evpn, bnc->nexthop->ifindex); + if (!vpn) + return false; + + if (vpn->bgp_vrf != bnc->bgp) + return false; + + /* + * Check if the gateway IP is present in the EVI remote_ip_hash table + * which stores all the remote IP addresses received via MAC/IP routes + * in this EVI + */ + memset(&tmp, 0, sizeof(struct evpn_remote_ip)); + + p = &bnc->prefix; + if (p->family == AF_INET) { + tmp.addr.ipa_type = IPADDR_V4; + memcpy(&(tmp.addr.ipaddr_v4), &(p->u.prefix4), + sizeof(struct in_addr)); + } else if (p->family == AF_INET6) { + tmp.addr.ipa_type = IPADDR_V6; + memcpy(&(tmp.addr.ipaddr_v6), &(p->u.prefix6), + sizeof(struct in6_addr)); + } else + return false; + + if (hash_lookup(vpn->remote_ip_hash, &tmp) == NULL) + return false; + + return true; +} + +/* Resolve/Unresolve nexthops when a MAC/IP route is added/deleted */ +static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn, + struct ipaddr *addr, + bool resolve) +{ + afi_t afi; + struct prefix p; + struct bgp_nexthop_cache *bnc; + struct bgp_nexthop_cache_head *tree = NULL; + + if (!vpn->bgp_vrf || vpn->svi_ifindex == 0) + return; + + memset(&p, 0, sizeof(struct prefix)); + + if (addr->ipa_type == IPADDR_V4) { + afi = AFI_IP; + p.family = AF_INET; + memcpy(&(p.u.prefix4), &(addr->ipaddr_v4), + sizeof(struct in_addr)); + p.prefixlen = IPV4_MAX_BITLEN; + } else if (addr->ipa_type == IPADDR_V6) { + afi = AFI_IP6; + p.family = AF_INET6; + memcpy(&(p.u.prefix6), &(addr->ipaddr_v6), + sizeof(struct in6_addr)); + p.prefixlen = IPV6_MAX_BITLEN; + } else + return; + + tree = &vpn->bgp_vrf->nexthop_cache_table[afi]; + bnc = bnc_find(tree, &p, 0); + + if (!bnc || !bnc->is_evpn_gwip_nexthop) + return; + + if (!bnc->nexthop || bnc->nexthop->ifindex != vpn->svi_ifindex) + return; + + if (BGP_DEBUG(nht, NHT)) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&bnc->prefix, buf, sizeof(buf)); + zlog_debug("%s(%u): vni %u mac/ip %s for NH %s", + vpn->bgp_vrf->name_pretty, vpn->tenant_vrf_id, + vpn->vni, (resolve ? "add" : "delete"), buf); + } + + /* + * MAC/IP route or SVI or tenant vrf being added to EVI. + * Set nexthop as valid only if it is already L3 reachable + */ + if (resolve && bnc->flags & BGP_NEXTHOP_EVPN_INCOMPLETE) { + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->flags |= BGP_NEXTHOP_VALID; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + evaluate_paths(bnc); + } + + /* MAC/IP route or SVI or tenant vrf being deleted from EVI */ + if (!resolve && bnc->flags & BGP_NEXTHOP_VALID) { + bnc->flags &= ~BGP_NEXTHOP_VALID; + bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + evaluate_paths(bnc); + } +} + +void bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket, + void *arg) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + struct bgp_dest *dest; + struct bgp_path_info *pi; + + bgp_evpn_remote_ip_hash_init(vpn); + + for (dest = bgp_table_top(vpn->route_table); dest; + dest = bgp_route_next(dest)) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) + bgp_evpn_remote_ip_hash_add(vpn, pi); +} + +void bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket, + void *arg) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + + bgp_evpn_remote_ip_hash_destroy(vpn); +} diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 83a6dd84c..eec746e3b 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -63,14 +63,18 @@ static inline int advertise_type5_routes(struct bgp *bgp_vrf, if (!bgp_vrf->l3vni) return 0; - if (afi == AFI_IP && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) + if ((afi == AFI_IP) + && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) + || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)))) return 1; - if (afi == AFI_IP6 && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) + if ((afi == AFI_IP6) + && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) + || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)))) return 1; return 0; @@ -152,6 +156,14 @@ static inline bool is_route_injectable_into_evpn(struct bgp_path_info *pi) return true; } +static inline bool evpn_resolve_overlay_index(void) +{ + struct bgp *bgp = NULL; + + bgp = bgp_get_evpn(); + return bgp ? bgp->resolve_overlay_index : false; +} + extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, const struct prefix *p, struct attr *src_attr, afi_t afi, @@ -198,7 +210,8 @@ extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni); extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp); + struct in_addr mcast_grp, + ifindex_t svi_ifindex); extern void bgp_evpn_flood_control_change(struct bgp *bgp); extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp); extern void bgp_evpn_cleanup(struct bgp *bgp); @@ -206,4 +219,15 @@ extern void bgp_evpn_init(struct bgp *bgp); extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx); extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx); extern void update_advertise_vrf_routes(struct bgp *bgp_vrf); +extern void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, + void *args); +extern void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args); +extern bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc); +extern void +bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket, + void *arg); +extern void +bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket, + void *arg); + #endif /* _QUAGGA_BGP_EVPN_H */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index debed9f68..c46f34bf7 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -62,6 +62,7 @@ RB_PROTOTYPE(bgp_es_evi_rb_head, bgp_evpn_es_evi, rb_node, struct bgpevpn { vni_t vni; vrf_id_t tenant_vrf_id; + ifindex_t svi_ifindex; uint32_t flags; #define VNI_FLAG_CFGD 0x1 /* VNI is user configured */ #define VNI_FLAG_LIVE 0x2 /* VNI is "live" */ @@ -102,6 +103,15 @@ struct bgpevpn { struct list *import_rtl; struct list *export_rtl; + /* + * EVPN route that uses gateway IP overlay index as its nexthop + * needs to do a recursive lookup. + * A remote MAC/IP entry should be present for the gateway IP. + * Maintain a hash of the addresses received via remote MAC/IP routes + * for efficient gateway IP recursive lookup in this EVI + */ + struct hash *remote_ip_hash; + /* Route table for EVPN routes for * this VNI. */ struct bgp_table *route_table; @@ -178,6 +188,12 @@ struct bgp_evpn_info { bool is_anycast_mac; }; +/* This structure defines an entry in remote_ip_hash */ +struct evpn_remote_ip { + struct ipaddr addr; + struct list *macip_path_list; +}; + static inline int is_vrf_rd_configured(struct bgp *bgp_vrf) { return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD)); @@ -612,7 +628,8 @@ extern struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni); extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp); + struct in_addr mcast_grp, + ifindex_t svi_ifindex); extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn); extern bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni); extern int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 2a7c2ec85..bc66fb9c1 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -59,6 +59,17 @@ struct vni_walk_ctx { int detail; }; +int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, int *oly_idx, + enum overlay_index_type *oly) +{ + *oly = OVERLAY_INDEX_TYPE_NONE; + if (argv_find(argv, argc, "gateway-ip", oly_idx)) { + if (oly) + *oly = OVERLAY_INDEX_GATEWAY_IP; + } + return 1; +} + static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt, json_object *json) { @@ -520,6 +531,9 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) else json_object_string_add(json, "advertiseSviMacIp", "Disabled"); + json_object_string_add( + json, "sviInterface", + ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id)); } else { vty_out(vty, "VNI: %d", vpn->vni); if (is_vni_live(vpn)) @@ -553,6 +567,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) else vty_out(vty, " Advertise-svi-macip : %s\n", "Disabled"); + vty_out(vty, " SVI interface : %s\n", + ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id)); } if (!json) @@ -2279,7 +2295,7 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni) /* tenant vrf will be updated when we get local_vni_add from * zebra */ - vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp); + vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp, 0); if (!vpn) { flog_err( EC_BGP_VNI, @@ -3286,6 +3302,28 @@ static void evpn_unset_advertise_all_vni(struct bgp *bgp) bgp_evpn_cleanup_on_disable(bgp); } +/* Set resolve overlay index flag */ +static void bgp_evpn_set_unset_resolve_overlay_index(struct bgp *bgp, bool set) +{ + if (set == bgp->resolve_overlay_index) + return; + + if (set) { + bgp->resolve_overlay_index = true; + hash_iterate(bgp->vnihash, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_handle_resolve_overlay_index_set, + NULL); + } else { + hash_iterate( + bgp->vnihash, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_handle_resolve_overlay_index_unset, + NULL); + bgp->resolve_overlay_index = false; + } +} + /* * EVPN - use RFC8365 to auto-derive RT */ @@ -3784,10 +3822,11 @@ DEFUN_HIDDEN (no_bgp_evpn_advertise_vni_subnet, DEFUN (bgp_evpn_advertise_type5, bgp_evpn_advertise_type5_cmd, - "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]", + "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [gateway-ip] [route-map WORD]", "Advertise prefix routes\n" BGP_AFI_HELP_STR BGP_SAFI_HELP_STR + "advertise gateway IP overlay index\n" "route-map for filtering specific routes\n" "Name of the route map\n") { @@ -3799,9 +3838,14 @@ DEFUN (bgp_evpn_advertise_type5, safi_t safi = 0; int ret = 0; int rmap_changed = 0; + enum overlay_index_type oly = OVERLAY_INDEX_TYPE_NONE; + int idx_oly = 0; + bool adv_flag_changed = false; argv_find_and_parse_afi(argv, argc, &idx_afi, &afi); argv_find_and_parse_safi(argv, argc, &idx_safi, &safi); + argv_find_and_parse_oly_idx(argv, argc, &idx_oly, &oly); + ret = argv_find(argv, argc, "route-map", &idx_rmap); if (ret) { if (!bgp_vrf->adv_cmd_rmap[afi][safi].name) @@ -3826,39 +3870,149 @@ DEFUN (bgp_evpn_advertise_type5, return CMD_WARNING; } + if ((oly != OVERLAY_INDEX_TYPE_NONE) + && (oly != OVERLAY_INDEX_GATEWAY_IP)) { + vty_out(vty, "%%Unknown overlay-index type specified"); + return CMD_WARNING; + } + if (afi == AFI_IP) { + if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) + && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) { + + /* + * this is the case for first time ever configuration + * adv ipv4 unicast is enabled for the first time. + * So no need to reset any flag + */ + if (oly == OVERLAY_INDEX_TYPE_NONE) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + else if (oly == OVERLAY_INDEX_GATEWAY_IP) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); + } else if ((oly == OVERLAY_INDEX_TYPE_NONE) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))) { + + /* + * This is modify case from gateway-ip + * to no overlay index + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + } else if ((oly == OVERLAY_INDEX_GATEWAY_IP) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) { + + /* + * This is modify case from no overlay index + * to gateway-ip + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); + } else { - /* if we are already advertising ipv4 prefix as type-5 - * nothing to do - */ - if (!rmap_changed && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) - return CMD_WARNING; - SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST); + /* + * Command is issued with the same option + * (no overlay index or gateway-ip) which was + * already configured. So nothing to do. + * However, route-map may have been modified. + * check if route-map has been modified. + * If not, return an error + */ + if (!rmap_changed) + return CMD_WARNING; + } } else { + if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) + && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) { + + /* + * this is the case for first time ever configuration + * adv ipv6 unicast is enabled for the first time. + * So no need to reset any flag + */ + if (oly == OVERLAY_INDEX_TYPE_NONE) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + else if (oly == OVERLAY_INDEX_GATEWAY_IP) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); + } else if ((oly == OVERLAY_INDEX_TYPE_NONE) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))) { + + /* + * This is modify case from gateway-ip + * to no overlay index + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + } else if ((oly == OVERLAY_INDEX_GATEWAY_IP) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) { + + /* + * This is modify case from no overlay index + * to gateway-ip + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); + } else { - /* if we are already advertising ipv6 prefix as type-5 - * nothing to do - */ - if (!rmap_changed && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) - return CMD_WARNING; - SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST); + /* + * Command is issued with the same option + * (no overlay index or gateway-ip) which was + * already configured. So nothing to do. + * However, route-map may have been modified. + * check if route-map has been modified. + * If not, return an error + */ + if (!rmap_changed) + return CMD_WARNING; + } } - if (rmap_changed) { + if ((rmap_changed) || (adv_flag_changed)) { + + /* If either of these are changed, then FRR needs to + * withdraw already advertised type5 routes. + */ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); - if (bgp_vrf->adv_cmd_rmap[afi][safi].name) { - XFREE(MTYPE_ROUTE_MAP_NAME, - bgp_vrf->adv_cmd_rmap[afi][safi].name); - route_map_counter_decrement( + if (rmap_changed) { + if (bgp_vrf->adv_cmd_rmap[afi][safi].name) { + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp_vrf->adv_cmd_rmap[afi][safi].name); + route_map_counter_decrement( bgp_vrf->adv_cmd_rmap[afi][safi].map); - bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL; - bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL; + bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL; + bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL; + } } } @@ -3912,22 +4066,30 @@ DEFUN (no_bgp_evpn_advertise_type5, /* if we are not advertising ipv4 prefix as type-5 * nothing to do */ - if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) { + if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) || + (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) { bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST); + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); } } else { /* if we are not advertising ipv6 prefix as type-5 * nothing to do */ - if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) { + if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) || + (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))){ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST); + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); } } @@ -3977,6 +4139,23 @@ DEFPY (bgp_evpn_ead_evi_tx_disable, return CMD_SUCCESS; } +DEFPY (bgp_evpn_enable_resolve_overlay_index, + bgp_evpn_enable_resolve_overlay_index_cmd, + "[no$no] enable-resolve-overlay-index", + NO_STR + "Enable Recursive Resolution of type-5 route overlay index\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + + if (bgp != bgp_get_evpn()) { + vty_out(vty, "This command is only supported under EVPN VRF\n"); + return CMD_WARNING; + } + + bgp_evpn_set_unset_resolve_overlay_index(bgp, no ? false : true); + return CMD_SUCCESS; +} + DEFPY (bgp_evpn_advertise_pip_ip_mac, bgp_evpn_advertise_pip_ip_mac_cmd, "[no$no] advertise-pip [ip <A.B.C.D> [mac <X:X:X:X:X:X|X:X:X:X:X:X/M>]]", @@ -4220,6 +4399,61 @@ DEFUN(show_bgp_l2vpn_evpn_vni, return CMD_SUCCESS; } +DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_remote_ip_hash, + show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd, + "show bgp l2vpn evpn vni remote-ip-hash", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show VNI\n" + "Remote IP hash\n") +{ + struct bgp *bgp_evpn; + int idx = 0; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return CMD_WARNING; + + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + hash_iterate(bgp_evpn->vnihash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_show_remote_ip_hash, + vty); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_svi_hash, + show_bgp_l2vpn_evpn_vni_svi_hash_cmd, + "show bgp l2vpn evpn vni-svi-hash", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show vni-svi-hash\n") +{ + struct bgp *bgp_evpn; + int idx = 0; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return CMD_WARNING; + + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + hash_iterate(bgp_evpn->vni_svi_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_show_vni_svi_hash, + vty); + + return CMD_SUCCESS; +} + DEFPY(show_bgp_l2vpn_evpn_es_evi, show_bgp_l2vpn_evpn_es_evi_cmd, "show bgp l2vpn evpn es-evi [vni (1-16777215)$vni] [json$uj] [detail$detail]", @@ -6061,6 +6295,9 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp->evpn_info->advertise_svi_macip) vty_out(vty, " advertise-svi-ip\n"); + if (bgp->resolve_overlay_index) + vty_out(vty, " enable-resolve-overlay-index\n"); + if (bgp_mh_info->host_routes_use_l3nhg != BGP_EVPN_MH_USE_ES_L3NHG_DEF) { if (bgp_mh_info->host_routes_use_l3nhg) @@ -6107,21 +6344,40 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, vty_out(vty, " flooding disable\n"); if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) { + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) { if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name) vty_out(vty, " advertise ipv4 unicast route-map %s\n", bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name); else - vty_out(vty, " advertise ipv4 unicast\n"); + vty_out(vty, + " advertise ipv4 unicast\n"); + } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) { + if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name) + vty_out(vty, + " advertise ipv4 unicast gateway-ip route-map %s\n", + bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name); + else + vty_out(vty, " advertise ipv4 unicast gateway-ip\n"); } if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) { + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) { if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name) - vty_out(vty, " advertise ipv6 unicast route-map %s\n", + vty_out(vty, + " advertise ipv6 unicast route-map %s\n", + bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name); + else + vty_out(vty, + " advertise ipv6 unicast\n"); + } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) { + if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name) + vty_out(vty, + " advertise ipv6 unicast gateway-ip route-map %s\n", bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name); else - vty_out(vty, " advertise ipv6 unicast\n"); + vty_out(vty, " advertise ipv6 unicast gateway-ip\n"); } if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], @@ -6230,6 +6486,8 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_NODE, &bgp_evpn_use_es_l3nhg_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_rx_disable_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_tx_disable_cmd); + install_element(BGP_EVPN_NODE, + &bgp_evpn_enable_resolve_overlay_index_cmd); /* test commands */ install_element(BGP_EVPN_NODE, &test_es_add_cmd); @@ -6241,6 +6499,8 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_vrf_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_nh_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_svi_hash_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd); diff --git a/bgpd/bgp_evpn_vty.h b/bgpd/bgp_evpn_vty.h index 33f6e4f1b..137365ddb 100644 --- a/bgpd/bgp_evpn_vty.h +++ b/bgpd/bgp_evpn_vty.h @@ -28,6 +28,10 @@ extern void bgp_ethernetvpn_init(void); #define L2VPN_HELP_STR "Layer 2 Virtual Private Network\n" #define EVPN_HELP_STR "Ethernet Virtual Private Network\n" +extern int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, + int *oly_idx, + enum overlay_index_type *oly); + /* Parse type from "type <ead|1|...>", return -1 on failure */ extern int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv, int argc); diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c index 3d7bc08ac..02b7e6486 100644 --- a/bgpd/bgp_mac.c +++ b/bgpd/bgp_mac.c @@ -200,8 +200,8 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, AFI_L2VPN, SAFI_EVPN, &prd, p, label_pnt, num_labels, pi->addpath_rx_id ? 1 : 0, - pi->addpath_rx_id, pfx_buf, - sizeof(pfx_buf)); + pi->addpath_rx_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s skip update of %s marked as removed", peer->host, pfx_buf); diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index eb85936f0..196e56a93 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -144,3 +144,4 @@ DEFINE_MTYPE(BGPD, BGP_SRV6_L3VPN, "BGP prefix-sid srv6 l3vpn servcie"); DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service"); DEFINE_MTYPE(BGPD, BGP_SRV6_SID, "BGP srv6 segment-id"); DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function"); +DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry"); diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index c5ba37149..0021fa9c3 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -142,4 +142,6 @@ DECLARE_MTYPE(BGP_SRV6_VPN); DECLARE_MTYPE(BGP_SRV6_SID); DECLARE_MTYPE(BGP_SRV6_FUNCTION); +DECLARE_MTYPE(EVPN_REMOTE_IP); + #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_nb_config.c b/bgpd/bgp_nb_config.c index 5189d7ba8..ab22aee1c 100644 --- a/bgpd/bgp_nb_config.c +++ b/bgpd/bgp_nb_config.c @@ -182,24 +182,52 @@ int bgp_router_destroy(struct nb_cb_destroy_args *args) for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) { if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF) continue; - if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_VRF_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_VRF_EXPORT) || - (bgp == bgp_get_evpn() && - (CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))) || - (tmp_bgp->vnihash && hashcount(tmp_bgp->vnihash))) { + if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT) + || (bgp == bgp_get_evpn() + && (CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST) + || CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP) + || CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST) + || CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) + || (tmp_bgp->vnihash + && hashcount(tmp_bgp->vnihash))) { snprintf( args->errmsg, args->errmsg_len, "Cannot delete default BGP instance. Dependent VRF instances exist\n"); diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index c417dda39..e7bad42f9 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -835,6 +835,18 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, bnc->metric, bnc->path_count); if (peer) vty_out(vty, ", peer %s", peer->host); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); + vty_out(vty, "\n"); + bgp_show_nexthops_detail(vty, bgp, bnc); + } else if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE)) { + vty_out(vty, + " %s overlay index unresolved [IGP metric %d], #paths %d", + inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, + buf, sizeof(buf)), + bnc->metric, bnc->path_count); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); vty_out(vty, "\n"); bgp_show_nexthops_detail(vty, bgp, bnc); } else { @@ -844,6 +856,8 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, bnc->path_count); if (peer) vty_out(vty, ", peer %s", peer->host); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); vty_out(vty, "\n"); if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) vty_out(vty, " Must be Connected\n"); diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index fe0a9646a..16c2b6c65 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -56,6 +56,10 @@ struct bgp_nexthop_cache { time_t last_update; uint16_t flags; +/* + * If the nexthop is EVPN gateway IP NH, VALID flag is set only if the nexthop + * is RIB reachable as well as MAC/IP is present + */ #define BGP_NEXTHOP_VALID (1 << 0) #define BGP_NEXTHOP_REGISTERED (1 << 1) #define BGP_NEXTHOP_CONNECTED (1 << 2) @@ -64,11 +68,29 @@ struct bgp_nexthop_cache { #define BGP_STATIC_ROUTE_EXACT_MATCH (1 << 5) #define BGP_NEXTHOP_LABELED_VALID (1 << 6) +/* + * This flag is added for EVPN gateway IP nexthops. + * If the nexthop is RIB reachable, but a MAC/IP is not yet + * resolved, this flag is set. + * Following table explains the combination of L3 and L2 reachability w.r.t. + * VALID and INCOMPLETE flags + * + * | MACIP resolved | MACIP unresolved + *----------------|----------------|------------------ + * L3 reachable | VALID = 1 | VALID = 0 + * | INCOMPLETE = 0 | INCOMPLETE = 1 + * ---------------|----------------|-------------------- + * L3 unreachable | VALID = 0 | VALID = 0 + * | INCOMPLETE = 0 | INCOMPLETE = 0 + */ +#define BGP_NEXTHOP_EVPN_INCOMPLETE (1 << 7) + uint16_t change_flags; #define BGP_NEXTHOP_CHANGED (1 << 0) #define BGP_NEXTHOP_METRIC_CHANGED (1 << 1) #define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2) +#define BGP_NEXTHOP_MACIP_CHANGED (1 << 3) /* Back pointer to the cache tree this entry belongs to. */ struct bgp_nexthop_cache_head *tree; @@ -79,6 +101,11 @@ struct bgp_nexthop_cache { LIST_HEAD(path_list, bgp_path_info) paths; unsigned int path_count; struct bgp *bgp; + + /* This flag is set to TRUE for a bnc that is gateway IP overlay index + * nexthop. + */ + bool is_evpn_gwip_nexthop; }; extern int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a, diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 4b4a3716e..742ef217d 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -53,7 +53,6 @@ static void register_zebra_rnh(struct bgp_nexthop_cache *bnc, int is_bgp_static_route); static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, int is_bgp_static_route); -static void evaluate_paths(struct bgp_nexthop_cache *bnc); static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p); static int bgp_nht_ifp_initial(struct thread *thread); @@ -244,6 +243,9 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, } } + if (pi && is_route_parent_evpn(pi)) + bnc->is_evpn_gwip_nexthop = true; + if (is_bgp_static_route) { SET_FLAG(bnc->flags, BGP_STATIC_ROUTE); @@ -331,7 +333,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, return 1; else if (safi == SAFI_UNICAST && pi && pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra - && pi->extra->num_labels) { + && pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) { return bgp_isvalid_labeled_nexthop(bnc); } else return (bgp_isvalid_nexthop(bnc)); @@ -387,6 +389,7 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, struct nexthop *nhlist_head = NULL; struct nexthop *nhlist_tail = NULL; int i; + bool evpn_resolved = false; bnc->last_update = bgp_clock(); bnc->change_flags = 0; @@ -417,7 +420,8 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, if (!bnc->nexthop_num) UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); - bnc->flags |= BGP_NEXTHOP_VALID; + if (!bnc->is_evpn_gwip_nexthop) + bnc->flags |= BGP_NEXTHOP_VALID; bnc->metric = nhr->metric; bnc->nexthop_num = nhr->nexthop_num; @@ -488,7 +492,40 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, } bnc_nexthop_free(bnc); bnc->nexthop = nhlist_head; + + /* + * Gateway IP nexthop is L3 reachable. Mark it as + * BGP_NEXTHOP_VALID only if it is recursively resolved with a + * remote EVPN RT-2. + * Else, mark it as BGP_NEXTHOP_EVPN_INCOMPLETE. + * When its mapping with EVPN RT-2 is established, unset + * BGP_NEXTHOP_EVPN_INCOMPLETE and set BGP_NEXTHOP_VALID. + */ + if (bnc->is_evpn_gwip_nexthop) { + evpn_resolved = bgp_evpn_is_gateway_ip_resolved(bnc); + + if (BGP_DEBUG(nht, NHT)) { + char buf2[PREFIX2STR_BUFFER]; + + prefix2str(&bnc->prefix, buf2, sizeof(buf2)); + zlog_debug( + "EVPN gateway IP %s recursive MAC/IP lookup %s", + buf2, + (evpn_resolved ? "successful" + : "failed")); + } + + if (evpn_resolved) { + bnc->flags |= BGP_NEXTHOP_VALID; + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + } else { + bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->flags &= ~BGP_NEXTHOP_VALID; + } + } } else { + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; bnc->flags &= ~BGP_NEXTHOP_VALID; bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID; bnc->nexthop_num = nhr->nexthop_num; @@ -694,6 +731,7 @@ void bgp_cleanup_nexthops(struct bgp *bgp) UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE); } } } @@ -888,7 +926,7 @@ static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, * RETURNS: * void. */ -static void evaluate_paths(struct bgp_nexthop_cache *bnc) +void evaluate_paths(struct bgp_nexthop_cache *bnc) { struct bgp_dest *dest; struct bgp_path_info *path; @@ -942,16 +980,18 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) * In case of unicast routes that were imported from vpn * and that have labels, they are valid only if there are * nexthops with labels + * + * If the nexthop is EVPN gateway-IP, + * do not check for a valid label. */ bool bnc_is_valid_nexthop = false; bool path_valid = false; - if (safi == SAFI_UNICAST && - path->sub_type == BGP_ROUTE_IMPORTED && - path->extra && - path->extra->num_labels) { - + if (safi == SAFI_UNICAST && path->sub_type == BGP_ROUTE_IMPORTED + && path->extra && path->extra->num_labels + && (path->attr->evpn_overlay.type + != OVERLAY_INDEX_GATEWAY_IP)) { bnc_is_valid_nexthop = bgp_isvalid_labeled_nexthop(bnc) ? true : false; } else { diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h index 9268b225c..e006aa446 100644 --- a/bgpd/bgp_nht.h +++ b/bgpd/bgp_nht.h @@ -90,6 +90,7 @@ extern void bgp_nht_register_nexthops(struct bgp *bgp); */ extern void bgp_nht_reg_enhe_cap_intfs(struct peer *peer); extern void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer); +extern void evaluate_paths(struct bgp_nexthop_cache *bnc); /* APIs for setting up and allocating L3 nexthop group ids */ extern uint32_t bgp_l3nhg_id_alloc(void); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index cf651185a..4637cef3e 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -3451,23 +3451,6 @@ struct bgp_path_info *info_make(int type, int sub_type, unsigned short instance, return new; } -static void overlay_index_update(struct attr *attr, - union gw_addr *gw_ip) -{ - if (!attr) - return; - if (gw_ip == NULL) { - struct bgp_route_evpn eo; - - memset(&eo, 0, sizeof(eo)); - bgp_attr_set_evpn_overlay(attr, &eo); - } else { - struct bgp_route_evpn eo = {.gw_ip = *gw_ip}; - - bgp_attr_set_evpn_overlay(attr, &eo); - } -} - static bool overlay_index_equal(afi_t afi, struct bgp_path_info *path, union gw_addr *gw_ip) { @@ -3650,6 +3633,11 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (has_valid_label) assert(label != NULL); + /* Update overlay index of the attribute */ + if (afi == AFI_L2VPN && evpn) + memcpy(&attr->evpn_overlay, evpn, + sizeof(struct bgp_route_evpn)); + /* When peer's soft reconfiguration enabled. Record input packet in Adj-RIBs-In. */ if (!soft_reconfig @@ -3825,12 +3813,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, goto filtered; } - /* Update Overlay Index */ - if (afi == AFI_L2VPN) { - overlay_index_update(&new_attr, - evpn == NULL ? NULL : &evpn->gw_ip); - } - /* The flag BGP_NODE_FIB_INSTALL_PENDING is for the following * condition : * Suppress fib is enabled @@ -3865,10 +3847,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, && (!has_valid_label || memcmp(&(bgp_path_info_extra_get(pi))->label, label, num_labels * sizeof(mpls_label_t)) - == 0) - && (overlay_index_equal( - afi, pi, - evpn == NULL ? NULL : &evpn->gw_ip))) { + == 0)) { if (get_active_bdc_from_pi(pi, afi, safi) && peer->sort == BGP_PEER_EBGP && CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) { @@ -3876,7 +3855,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, pfx_buf, + addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); @@ -3902,7 +3881,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, pfx_buf, + addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s rcvd %s...duplicate ignored", @@ -3929,8 +3908,8 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, evpn, + pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s rcvd %s, flapped quicker than processing", peer->host, pfx_buf); @@ -3943,7 +3922,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, pfx_buf, + addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); } @@ -4216,8 +4195,8 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, evpn, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); } @@ -4248,11 +4227,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } - /* Update Overlay Index */ - if (afi == AFI_L2VPN) { - overlay_index_update(new->attr, - evpn == NULL ? NULL : &evpn->gw_ip); - } /* Nexthop reachability check. */ if (((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) @@ -4362,8 +4336,8 @@ filtered: } bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, evpn, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd UPDATE about %s -- DENIED due to: %s", peer->host, pfx_buf, reason); } @@ -4447,8 +4421,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s withdrawing route %s not in adj-in", peer->host, pfx_buf); @@ -4467,8 +4441,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id, /* Logging. */ if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd UPDATE about %s -- withdrawn", peer->host, pfx_buf); } @@ -4488,8 +4462,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } else if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s Can't find the route %s", peer->host, pfx_buf); } @@ -9867,6 +9841,11 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, json_nexthop_global = json_object_new_object(); } + if (safi == SAFI_EVPN) { + if (!json_paths) + vty_out(vty, " Route %pRN", bn); + } + if (path->extra) { char tag_buf[30]; @@ -9878,12 +9857,8 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } if (safi == SAFI_EVPN) { if (!json_paths) { - vty_out(vty, " Route %pFX", - (struct prefix_evpn *) - bgp_dest_get_prefix(bn)); if (tag_buf[0] != '\0') vty_out(vty, " VNI %s", tag_buf); - vty_out(vty, "\n"); } else { if (tag_buf[0]) json_object_string_add(json_path, "VNI", @@ -9930,6 +9905,27 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } } + if (safi == SAFI_EVPN + && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) { + char gwip_buf[INET6_ADDRSTRLEN]; + + if (is_evpn_prefix_ipaddr_v4((struct prefix_evpn *)&bn->p)) + inet_ntop(AF_INET, &attr->evpn_overlay.gw_ip.ipv4, + gwip_buf, sizeof(gwip_buf)); + else + inet_ntop(AF_INET6, &attr->evpn_overlay.gw_ip.ipv6, + gwip_buf, sizeof(gwip_buf)); + + if (json_paths) + json_object_string_add(json_path, "gatewayIP", + gwip_buf); + else + vty_out(vty, " Gateway IP %s", gwip_buf); + } + + if (safi == SAFI_EVPN) + vty_out(vty, "\n"); + /* Line1 display AS-path, Aggregator */ if (attr->aspath) { if (json_paths) { diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 806a771cf..9a7b7b3cf 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1084,6 +1084,71 @@ static const struct route_map_rule_cmd route_match_evpn_rd_cmd = { route_match_rd_free }; +static enum route_map_cmd_result_t +route_set_evpn_gateway_ip(void *rule, const struct prefix *prefix, void *object) +{ + struct ipaddr *gw_ip = rule; + struct bgp_path_info *path; + struct prefix_evpn *evp; + + if (prefix->family != AF_EVPN) + return RMAP_OKAY; + + evp = (struct prefix_evpn *)prefix; + if (evp->prefix.route_type != BGP_EVPN_IP_PREFIX_ROUTE) + return RMAP_OKAY; + + if ((is_evpn_prefix_ipaddr_v4(evp) && IPADDRSZ(gw_ip) != 4) + || (is_evpn_prefix_ipaddr_v6(evp) && IPADDRSZ(gw_ip) != 16)) + return RMAP_OKAY; + + path = object; + + /* Set gateway-ip value. */ + path->attr->evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP; + memcpy(&path->attr->evpn_overlay.gw_ip, &gw_ip->ip.addr, + IPADDRSZ(gw_ip)); + + return RMAP_OKAY; +} + +/* + * Route map `evpn gateway-ip' compile function. + * Given string is converted to struct ipaddr structure + */ +static void *route_set_evpn_gateway_ip_compile(const char *arg) +{ + struct ipaddr *gw_ip = NULL; + int ret; + + gw_ip = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct ipaddr)); + + ret = str2ipaddr(arg, gw_ip); + if (ret < 0) { + XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip); + return NULL; + } + return gw_ip; +} + +/* Free route map's compiled `evpn gateway_ip' value. */ +static void route_set_evpn_gateway_ip_free(void *rule) +{ + struct ipaddr *gw_ip = rule; + + XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip); +} + +/* Route map commands for set evpn gateway-ip ipv4. */ +struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv4_cmd = { + "evpn gateway-ip ipv4", route_set_evpn_gateway_ip, + route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free}; + +/* Route map commands for set evpn gateway-ip ipv6. */ +struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv6_cmd = { + "evpn gateway-ip ipv6", route_set_evpn_gateway_ip, + route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free}; + /* Route map commands for VRF route leak with source vrf matching */ static enum route_map_cmd_result_t route_match_vrl_source_vrf(void *rule, const struct prefix *prefix, @@ -4067,6 +4132,148 @@ DEFUN_YANG (no_match_evpn_rd, return nb_cli_apply_changes(vty, NULL); } +DEFUN_YANG (set_evpn_gw_ip_ipv4, + set_evpn_gw_ip_ipv4_cmd, + "set evpn gateway-ip ipv4 A.B.C.D", + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv4 address\n" + "Gateway IP address in IPv4 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']"; + char xpath_value[XPATH_MAXLEN]; + + ret = str2sockunion(argv[4]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (su.sin.sin_addr.s_addr == 0 + || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) { + vty_out(vty, + "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4", + xpath); + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg); + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_set_evpn_gw_ip_ipv4, + no_set_evpn_gw_ip_ipv4_cmd, + "no set evpn gateway-ip ipv4 A.B.C.D", + NO_STR + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv4 address\n" + "Gateway IP address in IPv4 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']"; + + ret = str2sockunion(argv[5]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (su.sin.sin_addr.s_addr == 0 + || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) { + vty_out(vty, + "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (set_evpn_gw_ip_ipv6, + set_evpn_gw_ip_ipv6_cmd, + "set evpn gateway-ip ipv6 X:X::X:X", + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv6 address\n" + "Gateway IP address in IPv6 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']"; + char xpath_value[XPATH_MAXLEN]; + + ret = str2sockunion(argv[4]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) + || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) { + vty_out(vty, + "%% Gateway IP cannot be a linklocal or multicast address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6", + xpath); + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg); + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_set_evpn_gw_ip_ipv6, + no_set_evpn_gw_ip_ipv6_cmd, + "no set evpn gateway-ip ipv6 X:X::X:X", + NO_STR + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv4 address\n" + "Gateway IP address in IPv4 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']"; + + ret = str2sockunion(argv[5]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) + || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) { + vty_out(vty, + "%% Gateway IP cannot be a linklocal or multicast address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + DEFPY_YANG(match_vrl_source_vrf, match_vrl_source_vrf_cmd, "match source-vrf NAME$vrf_name", @@ -6124,6 +6331,8 @@ void bgp_route_map_init(void) route_map_install_match(&route_match_evpn_default_route_cmd); route_map_install_match(&route_match_vrl_source_vrf_cmd); + route_map_install_set(&route_set_evpn_gateway_ip_ipv4_cmd); + route_map_install_set(&route_set_evpn_gateway_ip_ipv6_cmd); route_map_install_set(&route_set_table_id_cmd); route_map_install_set(&route_set_srte_color_cmd); route_map_install_set(&route_set_ip_nexthop_cmd); @@ -6167,6 +6376,10 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_match_evpn_rd_cmd); install_element(RMAP_NODE, &match_evpn_default_route_cmd); install_element(RMAP_NODE, &no_match_evpn_default_route_cmd); + install_element(RMAP_NODE, &set_evpn_gw_ip_ipv4_cmd); + install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv4_cmd); + install_element(RMAP_NODE, &set_evpn_gw_ip_ipv6_cmd); + install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv6_cmd); install_element(RMAP_NODE, &match_vrl_source_vrf_cmd); install_element(RMAP_NODE, &no_match_vrl_source_vrf_cmd); diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index b165c5d0e..1254591b8 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -372,6 +372,20 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { } }, { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy, + } + }, + { .xpath = NULL, }, } diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index a15f52151..f0e492eb6 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -130,6 +130,14 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_mod int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy( + struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy( + struct nb_cb_destroy_args *args); #ifdef __cplusplus } diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index ff08c16a8..e541d117b 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -2637,3 +2637,107 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_spec { return lib_route_map_entry_set_destroy(args); } + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4 + */ +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "evpn gateway-ip ipv4"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv4", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6 + */ +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "evpn gateway-ip ipv6"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv6", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 6418decd1..038ef4f79 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -862,6 +862,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, label_pnt, num_labels, addpath_encode, addpath_tx_id, + &adv->baa->attr->evpn_overlay, pfx_buf, sizeof(pfx_buf)); zlog_debug("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s", subgrp->update_group->id, subgrp->id, @@ -1031,7 +1032,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, NULL, 0, addpath_encode, addpath_tx_id, - pfx_buf, sizeof(pfx_buf)); + NULL, pfx_buf, sizeof(pfx_buf)); zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE %s -- unreachable", subgrp->update_group->id, subgrp->id, pfx_buf); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index c2c114d2c..73ce2d9ae 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1058,9 +1058,19 @@ static bool update_ipv4nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, * connected routes leaked into a VRF. */ if (is_evpn) { - api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; - SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); - api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + + /* + * If the nexthop is EVPN overlay index gateway IP, + * treat the nexthop as NEXTHOP_TYPE_IPV4 + * Else, mark the nexthop as onlink. + */ + if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) + api_nh->type = NEXTHOP_TYPE_IPV4; + else { + api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); + api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + } } else if (nh_othervrf && api_nh->gate.ipv4.s_addr == INADDR_ANY) { api_nh->type = NEXTHOP_TYPE_IFINDEX; @@ -1085,9 +1095,19 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, api_nh->vrf_id = nh_bgp->vrf_id; if (is_evpn) { - api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; - SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); - api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + + /* + * If the nexthop is EVPN overlay index gateway IP, + * treat the nexthop as NEXTHOP_TYPE_IPV4 + * Else, mark the nexthop as onlink. + */ + if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) + api_nh->type = NEXTHOP_TYPE_IPV6; + else { + api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); + api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + } } else if (nh_othervrf) { if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) { api_nh->type = NEXTHOP_TYPE_IFINDEX; @@ -1392,8 +1412,13 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, api_nh->label_num = 1; api_nh->labels[0] = label; } - memcpy(&api_nh->rmac, &(mpinfo->attr->rmac), - sizeof(struct ethaddr)); + + if (is_evpn + && mpinfo->attr->evpn_overlay.type + != OVERLAY_INDEX_GATEWAY_IP) + memcpy(&api_nh->rmac, &(mpinfo->attr->rmac), + sizeof(struct ethaddr)); + api_nh->weight = nh_weight; if (mpinfo->extra @@ -2805,6 +2830,7 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) struct in_addr vtep_ip = {INADDR_ANY}; vrf_id_t tenant_vrf_id = VRF_DEFAULT; struct in_addr mcast_grp = {INADDR_ANY}; + ifindex_t svi_ifindex = 0; s = zclient->ibuf; vni = stream_getl(s); @@ -2812,6 +2838,7 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) vtep_ip.s_addr = stream_get_ipv4(s); stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t)); mcast_grp.s_addr = stream_get_ipv4(s); + stream_get(&svi_ifindex, s, sizeof(ifindex_t)); } bgp = bgp_lookup_by_vrf_id(vrf_id); @@ -2819,16 +2846,17 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) return 0; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s", - (cmd == ZEBRA_VNI_ADD) ? "add" : "del", - vrf_id_to_name(vrf_id), vni, - vrf_id_to_name(tenant_vrf_id)); + zlog_debug( + "Rx VNI %s VRF %s VNI %u tenant-vrf %s SVI ifindex %u", + (cmd == ZEBRA_VNI_ADD) ? "add" : "del", + vrf_id_to_name(vrf_id), vni, + vrf_id_to_name(tenant_vrf_id), svi_ifindex); if (cmd == ZEBRA_VNI_ADD) return bgp_evpn_local_vni_add( bgp, vni, vtep_ip.s_addr != INADDR_ANY ? vtep_ip : bgp->router_id, - tenant_vrf_id, mcast_grp); + tenant_vrf_id, mcast_grp, svi_ifindex); else return bgp_evpn_local_vni_del(bgp, vni); } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index b18cd4ca7..776f4b0a2 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -510,16 +510,18 @@ struct bgp { uint16_t af_flags[AFI_MAX][SAFI_MAX]; #define BGP_CONFIG_DAMPENING (1 << 0) /* l2vpn evpn flags - 1 << 0 is used for DAMPENNG */ -#define BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST (1 << 1) -#define BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST (1 << 2) -#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 3) -#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 4) +#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST (1 << 1) +#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP (1 << 2) +#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST (1 << 3) +#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP (1 << 4) +#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 5) +#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 6) /* import/export between address families */ -#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 5) -#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 6) +#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 7) +#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 8) /* vrf-route leaking flags */ -#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 7) -#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 8) +#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 9) +#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 10) /* BGP per AF peer count */ uint32_t af_peer_count[AFI_MAX][SAFI_MAX]; @@ -638,6 +640,14 @@ struct bgp { /* EVI hash table */ struct hash *vnihash; + /* + * VNI hash table based on SVI ifindex as its key. + * We use SVI ifindex as key to lookup a VNI table for gateway IP + * overlay index recursive lookup. + * For this purpose, a hashtable is added which optimizes this lookup. + */ + struct hash *vni_svi_hash; + /* EVPN enable - advertise gateway macip routes */ int advertise_gw_macip; @@ -683,6 +693,15 @@ struct bgp { /* Hash table of EVPN nexthops maintained per-tenant-VRF */ struct hash *evpn_nh_table; + /* + * Flag resolve_overlay_index is used for recursive resolution + * procedures for EVPN type-5 route's gateway IP overlay index. + * When this flag is set, we build remote-ip-hash for + * all L2VNIs and resolve overlay index nexthops using this hash. + * Overlay index nexthops remain unresolved if this flag is not set. + */ + bool resolve_overlay_index; + /* vrf flags */ uint32_t vrf_flags; #define BGP_VRF_AUTO (1 << 0) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 4e78900e8..7f23f7a63 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2696,6 +2696,115 @@ remote VTEP. Note that you should not enable both the advertise-svi-ip and the advertise-default-gw at the same time. +.. _bgp-evpn-overlay-index-gateway-ip: + +EVPN Overlay Index Gateway IP +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Draft https://tools.ietf.org/html/draft-ietf-bess-evpn-prefix-advertisement-11 +explains the use of overlay indexes for recursive route resolution for EVPN +type-5 route. + +We support gateway IP overlay index. +A gateway IP, advertised with EVPN prefix route, is used to find an EVPN MAC/IP +route with its IP field same as the gateway IP. This MAC/IP entry provides the +nexthop VTEP and the tunnel information required for the VxLAN encapsulation. + +Functionality: + +:: + + . +--------+ BGP +--------+ BGP +--------+ +--------+ + SN1 | | IPv4 | | EVPN | | | | + ======+ Host1 +------+ PE1 +------+ PE2 +------+ Host2 + + | | | | | | | | + +--------+ +--------+ +--------+ +--------+ + +Consider above topology where prefix SN1 is connected behind host1. Host1 +advertises SN1 to PE1 over BGP IPv4 session. PE1 advertises SN1 to PE2 using +EVPN type-5 route with host1 IP as the gateway IP. PE1 also advertises +Host1 MAC/IP as type-2 route which is used to resolve host1 gateway IP. + +PE2 receives this type-5 route and imports it into the vrf based on route +targets. BGP prefix imported into the vrf uses gateway IP as its BGP nexthop. +This route is installed into zebra if following conditions are satisfied: +1. Gateway IP nexthop is L3 reachable. +2. PE2 has received EVPN type-2 route with IP field set to gateway IP. + +Topology requirements: +1. This feature is supported for asymmetric routing model only. While + sending packets to SN1, ingress PE (PE2) performs routing and + egress PE (PE1) performs only bridging. +2. This feature supports only tratitional(non vlan-aware) bridge model. Bridge + interface associated with L2VNI is an L3 interface. i.e., this interface is + configured with an address in the L2VNI subnet. Note that the gateway IP + should also have an address in the same subnet. +3. As this feature works in asymmetric routing model, all L2VNIs and corresponding + VxLAN and bridge interfaces should be present at all the PEs. +4. L3VNI configuration is required to generate and import EVPN type-5 routes. + L3VNI VxLAN and bridge interfaces also should be present. + +A PE can use one of the following two mechanisms to advertise an EVPN type-5 +route with gateway IP. + +1. CLI to add gateway IP while generating EVPN type-5 route from a BGP IPv4/IPv6 +prefix: + +.. index:: advertise <ipv4|ipv6> unicast [gateway-ip] +.. clicmd:: [no] advertise <ipv4|ipv6> unicast [gateway-ip] + +When this CLI is configured for a BGP vrf under L2VPN EVPN address family, EVPN +type-5 routes are generated for BGP prefixes in the vrf. Nexthop of the BGP +prefix becomes the gateway IP of the corresponding type-5 route. + +If the above command is configured without the "gateway-ip" keyword, type-5 +routes are generated without overlay index. + +2. Add gateway IP to EVPN type-5 route using a route-map: + +.. index:: set evpn gateway-ip <ipv4|ipv6> <addr> +.. clicmd:: [no] set evpn gateway-ip <ipv4|ipv6> <addr> + +When route-map with above set clause is applied as outbound policy in BGP, it +will set the gateway-ip in EVPN type-5 NLRI. + +Example configuration: + +.. code-block:: frr + + router bgp 100 + neighbor 192.168.0.1 remote-as 101 + ! + address-family ipv4 l2vpn evpn + neighbor 192.168.0.1 route-map RMAP out + exit-address-family + ! + route-map RMAP permit 10 + set evpn gateway-ip 10.0.0.1 + set evpn gateway-ip 10::1 + +A PE that receives a type-5 route with gateway IP overlay index should have +"enable-resolve-overlay-index" configuration enabled to recursively resolve the +overlay index nexthop and install the prefix into zebra. + +.. index:: enable-resolve-overlay-index +.. clicmd:: [no] enable-resolve-overlay-index + +Example configuration: + +.. code-block:: frr + + router bgp 65001 + bgp router-id 192.168.100.1 + no bgp ebgp-requires-policy + neighbor 10.0.1.2 remote-as 65002 + ! + address-family l2vpn evpn + neighbor 10.0.1.2 activate + advertise-all-vni + enable-resolve-overlay-index + exit-address-family + ! + EVPN Multihoming ^^^^^^^^^^^^^^^^ diff --git a/lib/routemap.h b/lib/routemap.h index 5b6b64eae..4d76ae153 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -366,6 +366,10 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(A, "frr-bgp-route-map:ipv4-vpn-address")) #define IS_SET_BGP_IPV4_NH(A) \ (strmatch(A, "frr-bgp-route-map:set-ipv4-nexthop")) +#define IS_SET_BGP_EVPN_GATEWAY_IP_IPV4(A) \ + (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv4")) +#define IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(A) \ + (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv6")) /* Prototypes. */ extern void route_map_init(void); diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index e11b9eea7..bf982cfa2 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -1248,6 +1248,16 @@ void route_map_action_show(struct vty *vty, struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-set-action/frr-bgp-route-map:ipv4-nexthop")); + } else if (IS_SET_BGP_EVPN_GATEWAY_IP_IPV4(action)) { + vty_out(vty, " set evpn gateway-ip ipv4 %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4")); + } else if (IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(action)) { + vty_out(vty, " set evpn gateway-ip ipv6 %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6")); } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json new file mode 100644 index 000000000..2eeebad4b --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json new file mode 100644 index 000000000..419bcc3dd --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json @@ -0,0 +1,8 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]": null, + "[3]:[0]:[32]:[10.100.0.1]": null, + "[3]:[0]:[32]:[10.100.0.2]": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json new file mode 100644 index 000000000..2eeebad4b --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json new file mode 100644 index 000000000..833f98657 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"50.0.1.11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json new file mode 100644 index 000000000..833f98657 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"50.0.1.11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json new file mode 100644 index 000000000..4a292bddb --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": null } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json new file mode 100644 index 000000000..3dc3fcf9c --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"50:0:1::11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global" + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json new file mode 100644 index 000000000..3dc3fcf9c --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"50:0:1::11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global" + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json new file mode 100644 index 000000000..6c11d894e --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": null } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf new file mode 100644 index 000000000..63aa99a83 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf @@ -0,0 +1,30 @@ +router bgp 101 + bgp router-id 10.100.0.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.2 remote-as 102 + ! + address-family l2vpn evpn + neighbor 10.0.1.2 activate + advertise-all-vni + exit-address-family +! +router bgp 101 vrf vrf-blue + bgp router-id 10.100.0.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 50.0.1.11 remote-as 111 + neighbor 50:0:1::11 remote-as 111 + ! + address-family ipv4 unicast + no neighbor 50:0:1::11 activate + exit-address-family + ! + address-family ipv6 unicast + neighbor 50:0:1::11 activate + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast gateway-ip + advertise ipv6 unicast gateway-ip + exit-address-family
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf new file mode 100644 index 000000000..99a2e89ef --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf @@ -0,0 +1,14 @@ +! +log file zebra.log +! +ip route 10.100.0.2/32 10.0.1.2 +! +vrf vrf-blue + vni 1000 prefix-routes-only + exit-vrf +! +interface lo + ip address 10.100.0.1/32 +interface PE1-eth0 + ip address 10.0.1.1/24 +! diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json new file mode 100644 index 000000000..2dcf35d91 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json new file mode 100644 index 000000000..2dcf35d91 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json new file mode 100644 index 000000000..9c3091dc5 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +} diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json new file mode 100644 index 000000000..229c92765 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json @@ -0,0 +1,55 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json new file mode 100644 index 000000000..229c92765 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json @@ -0,0 +1,55 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json new file mode 100644 index 000000000..94f82e6d4 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json new file mode 100644 index 000000000..7b8d38e49 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json new file mode 100644 index 000000000..6273b3e72 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json @@ -0,0 +1,68 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]": null, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json new file mode 100644 index 000000000..7b8d38e49 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json new file mode 100644 index 000000000..c03d70195 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json new file mode 100644 index 000000000..7f1b8d2ef --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":null, + "bestpath":null, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json new file mode 100644 index 000000000..52e431163 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": null } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json new file mode 100644 index 000000000..1d90c9c79 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json @@ -0,0 +1,28 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } +] } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json new file mode 100644 index 000000000..a0e63c6e2 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json @@ -0,0 +1,28 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":null, + "bestpath":null, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json new file mode 100644 index 000000000..789fe69b2 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": null } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf new file mode 100644 index 000000000..59fee15df --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 102 + bgp router-id 10.100.0.2 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.1 remote-as 101 + ! + address-family l2vpn evpn + neighbor 10.0.1.1 activate + advertise-all-vni + enable-resolve-overlay-index + exit-address-family +! +router bgp 101 vrf vrf-blue + bgp router-id 10.100.0.2 diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf new file mode 100644 index 000000000..b78cdcc51 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf @@ -0,0 +1,14 @@ +! +log file zebra.log +! +ip route 10.100.0.1/32 10.0.1.1 +! +vrf vrf-blue + vni 1000 prefix-routes-only + exit-vrf +! +interface lo + ip address 10.100.0.2/32 +interface PE2-eth0 + ip address 10.0.1.2/24 +! diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json new file mode 100644 index 000000000..b3a3640be --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":40, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json new file mode 100644 index 000000000..996fe52f4 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json new file mode 100644 index 000000000..996fe52f4 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json new file mode 100644 index 000000000..d5be22a2b --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json @@ -0,0 +1,56 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":40, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50:0:1::11", + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json new file mode 100644 index 000000000..94f82e6d4 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json new file mode 100644 index 000000000..94f82e6d4 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py b/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf new file mode 100644 index 000000000..7608ec95c --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 111 + bgp router-id 10.100.0.11 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 50.0.1.1 remote-as 101 + neighbor 50:0:1::1 remote-as 101 + ! + address-family ipv4 unicast + network 100.0.0.21/32 + no neighbor 50:0:1::1 activate + exit-address-family + ! + address-family ipv6 unicast + network 100::21/128 + neighbor 50:0:1::1 activate + exit-address-family + + diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf new file mode 100644 index 000000000..c8c832e9d --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf @@ -0,0 +1,4 @@ +! +int host1-eth0 + ip address 50.0.1.11/24 + ipv6 address 50:0:1::11/48 diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf new file mode 100644 index 000000000..cdf4cb4fe --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf new file mode 100644 index 000000000..9135545c5 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf @@ -0,0 +1,4 @@ +! +int host1-eth0 + ip address 50.0.1.21/24 + ipv6 address 50:0:1::21/48 diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py b/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py new file mode 100755 index 000000000..fbce2809e --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_evpn_overlay_index_gateway.py: Test EVPN gateway IP overlay index functionality +Following functionality is covered: + + +--------+ BGP +--------+ BGP +--------+ +--------+ + SN1 | | IPv4/v6 | | EVPN | | | | + ======+ Host1 +---------+ PE1 +------+ PE2 +------+ Host2 + + | | | | | | | | + +--------+ +--------+ +--------+ +--------+ + + Host1 is connected to PE1 and host2 is connected to PE2 + Host1 and PE1 have IPv4/v6 BGP sessions. + PE1 and PE2 gave EVPN session. + Host1 advertises IPv4/v6 prefixes to PE1. + PE1 advertises these prefixes to PE2 as EVPN type-5 routes. + Gateway IP for these EVPN type-5 routes is host1 IP. + Host1 MAC/IP is advertised by PE1 as EVPN type-2 route + +Following testcases are covered: +TC_1: +Check BGP and zebra states for above topology at PE1 and PE2. + +TC_2: +Stop advertising prefixes from host1. It should withdraw type-5 routes. Check states at PE1 and PE2 +Advertise the prefixes again. Check states. + +TC_3: +Shut down VxLAN interface at PE1. This should withdraw type-2 routes. Check states at PE1 and PE2. +Enable VxLAN interface again. Check states. +""" + +import os +import sys +import json +from functools import partial +import pytest +import time +import platform + +#Current Working Directory +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import ( + step, + write_test_header, + write_test_footer, + generate_support_bundle, +) + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +#Global variables +PES = ['PE1', 'PE2'] +HOSTS = ['host1', 'host2'] +PE_SUFFIX = {'PE1': '1', 'PE2': '2'} +HOST_SUFFIX = {'host1': '1', 'host2': '2'} +TRIGGERS = ["base", "no_rt5", "no_rt2"] + + +class TemplateTopo(Topo): + """Test topology builder""" + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # This function only purpose is to define allocation and relationship + # between routers and add links. + + # Create routers + for pe in PES: + tgen.add_router(pe) + for host in HOSTS: + tgen.add_router(host) + + krel = platform.release() + logger.info('Kernel version ' + krel) + + #Add links + tgen.add_link(tgen.gears['PE1'], tgen.gears['PE2'], 'PE1-eth0', 'PE2-eth0') + tgen.add_link(tgen.gears['PE1'], tgen.gears['host1'], 'PE1-eth1', 'host1-eth0') + tgen.add_link(tgen.gears['PE2'], tgen.gears['host2'], 'PE2-eth1', 'host2-eth0') + + +def setup_module(mod): + "Sets up the pytest environment" + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15. Kernel present {}".format(kernelv)) + return + + if topotest.version_cmp(kernelv, '4.15') == 0: + l3mdev_accept = 1 + logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) + else: + l3mdev_accept = 0 + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + tgen.start_topology() + + # Configure MAC address for hosts as these MACs are advertised with EVPN type-2 routes + for (name, host) in tgen.gears.items(): + if name not in HOSTS: + continue + + host_mac = "1a:2b:3c:4d:5e:6{}".format(HOST_SUFFIX[name]) + host.run("ip link set dev {}-eth0 down").format(name) + host.run("ip link set dev {0}-eth0 address {1}".format(name, host_mac)) + host.run("ip link set dev {}-eth0 up").format(name) + + # Configure PE VxLAN and Bridge interfaces + for (name, pe) in tgen.gears.items(): + if name not in PES: + continue + vtep_ip = "10.100.0.{}".format(PE_SUFFIX[name]) + bridge_ip = "50.0.1.{}/24".format(PE_SUFFIX[name]) + bridge_ipv6 = "50:0:1::{}/48".format(PE_SUFFIX[name]) + + pe.run("ip link add vrf-blue type vrf table 10") + pe.run("ip link set dev vrf-blue up") + pe.run("ip link add vxlan100 type vxlan id 100 dstport 4789 local {}".format(vtep_ip)) + pe.run("ip link add name br100 type bridge stp_state 0") + pe.run("ip link set dev vxlan100 master br100") + pe.run("ip link set dev {}-eth1 master br100".format(name)) + pe.run("ip addr add {} dev br100".format(bridge_ip)) + pe.run("ip link set up dev br100") + pe.run("ip link set up dev vxlan100") + pe.run("ip link set up dev {}-eth1".format(name)) + pe.run("ip link set dev br100 master vrf-blue") + pe.run("ip -6 addr add {} dev br100".format(bridge_ipv6)) + + pe.run("ip link add vxlan1000 type vxlan id 1000 dstport 4789 local {}".format(vtep_ip)) + pe.run("ip link add name br1000 type bridge stp_state 0") + pe.run("ip link set dev vxlan1000 master br100") + pe.run("ip link set up dev br1000") + pe.run("ip link set up dev vxlan1000") + pe.run("ip link set dev br1000 master vrf-blue") + + pe.run("sysctl -w net.ipv4.ip_forward=1") + pe.run("sysctl -w net.ipv6.conf.all.forwarding=1") + pe.run("sysctl -w net.ipv4.udp_l3mdev_accept={}".format(l3mdev_accept)) + pe.run("sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept)) + + # For all registred routers, load the zebra configuration file + for (name, router) in tgen.routers().items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(name)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(name)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + logger.info("Running setup_module() done") + topotest.sleep(200) + + +def teardown_module(mod): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def evpn_gateway_ip_show_op_check(trigger=" "): + """ + This function checks CLI O/P for commands mentioned in show_commands for a given trigger + :param trigger: Should be a trigger present in TRIGGERS + :return: Returns a tuple (result: None for success, retmsg: Log message to be printed on failure) + """ + tgen = get_topogen() + + if trigger not in TRIGGERS: + return "Unexpected trigger", "Unexpected trigger {}".format(trigger) + + show_commands = {'bgp_vni_routes': 'show bgp l2vpn evpn route vni 100 json', + 'bgp_vrf_ipv4' : 'show bgp vrf vrf-blue ipv4 json', + 'bgp_vrf_ipv6' : 'show bgp vrf vrf-blue ipv6 json', + 'zebra_vrf_ipv4': 'show ip route vrf vrf-blue json', + 'zebra_vrf_ipv6': 'show ipv6 route vrf vrf-blue json'} + + for (name, pe) in tgen.gears.items(): + if name not in PES: + continue + + for (cmd_key, command) in show_commands.items(): + expected_op_file = "{0}/{1}/{2}_{3}.json".format(CWD, name, cmd_key, trigger) + expected_op = json.loads(open(expected_op_file).read()) + + test_func = partial(topotest.router_json_cmp, pe, command, expected_op) + ret, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{0}" JSON output mismatch for {1}'.format(name, command) + if result is not None: + return result, assertmsg + + return None, "Pass" + + +def test_evpn_gateway_ip_basic_topo(request): + """ + Tets EVPN overlay index gateway IP functionality. VErify show O/Ps on PE1 and PE2 + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Check O/Ps for EVPN gateway IP overlay Index functionality at PE1 and PE2") + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_evpn_gateway_ip_flap_rt5(request): + """ + Withdraw EVPN type-5 routes and check O/Ps at PE1 and PE2 + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + h1 = tgen.gears['host1'] + + step("Withdraw type-5 routes") + + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv4" \ + -c "no network 100.0.0.21/32"') + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv6" \ + -c "no network 100::21/128"') + + result, assertmsg = evpn_gateway_ip_show_op_check("no_rt5") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + step("Advertise type-5 routes again") + + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv4" \ + -c "network 100.0.0.21/32"') + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv6" \ + -c "network 100::21/128"') + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + if result is not None: + generate_support_bundle() + + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_evpn_gateway_ip_flap_rt2(request): + """ + Withdraw EVPN type-2 routes and check O/Ps at PE1 and PE2 + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + + step("Shut down VxLAN interface at PE1 which results in withdraw of type-2 routes") + + pe1 = tgen.gears['PE1'] + + pe1.run('ip link set dev vxlan100 down') + + result, assertmsg = evpn_gateway_ip_show_op_check("no_rt2") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + step("Bring up VxLAN interface at PE1 and advertise type-2 routes again") + + pe1.run('ip link set dev vxlan100 up') + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_memory_leak(): + """Run the memory leak test and report results""" + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf index 0d6432048..732470f82 100644 --- a/tools/etc/frr/support_bundle_commands.conf +++ b/tools/etc/frr/support_bundle_commands.conf @@ -31,7 +31,29 @@ show bgp ipv6 statistics show bgp martian next-hop show bgp nexthop +show bgp vrf all summary +show bgp vrf all ipv4 +show bgp vrf all ipv6 +show bgp vrf all neighbors + show bgp evpn route +show bgp l2vpn evpn route vni all +show bgp l2vpn evpn vni +show bgp l2vpn evpn import-rt +show bgp l2vpn evpn vrf-import-rt +show bgp l2vpn evpn all overlay +show bgp l2vpn evpn summary +show bgp l2vpn evpn route detail +show bgp l2vpn evpn vni remote-ip-hash +show bgp l2vpn evpn vni-svi-hash + +show evpn +show evpn arp-cache vni all detail +show evpn mac vni all detail +show evpn next-hops vni all +show evpn rmac vni all +show evpn vni detail + CMD_LIST_END # Zebra Support Bundle Command List diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index ca60c8f7b..1c990b5ed 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -300,6 +300,18 @@ module frr-bgp-route-map { "Set BGP large community list (for deletion)"; } + identity set-evpn-gateway-ip-ipv4 { + base frr-route-map:rmap-set-type; + description + "Set EVPN gateway IP overlay index IPv4"; + } + + identity set-evpn-gateway-ip-ipv6 { + base frr-route-map:rmap-set-type; + description + "Set EVPN gateway IP overlay index IPv6"; + } + grouping extcommunity-non-transitive-types { leaf two-octet-as-specific { type boolean; @@ -816,5 +828,25 @@ module frr-bgp-route-map { type bgp-filter:bgp-list-name; } } + case evpn-gateway-ip-ipv4 { + when + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, + 'frr-bgp-route-map:set-evpn-gateway-ip-ipv4')"; + description + "Set EVPN gateway IP overlay index IPv4"; + leaf evpn-gateway-ip-ipv4 { + type inet:ipv4-address; + } + } + case evpn-gateway-ip-ipv6 { + when + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, + 'frr-bgp-route-map:set-evpn-gateway-ip-ipv6')"; + description + "Set EVPN gateway IP overlay index IPv6"; + leaf evpn-gateway-ip-ipv6 { + type inet:ipv6-address; + } + } } } diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 30f4a4476..816f46bac 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -134,6 +134,10 @@ void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt) if (json == NULL) { vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name); vty_out(vty, " VxLAN ifIndex: %u\n", zevpn->vxlan_if->ifindex); + vty_out(vty, " SVI interface: %s\n", + (zevpn->svi_if ? zevpn->svi_if->name : "")); + vty_out(vty, " SVI ifIndex: %u\n", + (zevpn->svi_if ? zevpn->svi_if->ifindex : 0)); vty_out(vty, " Local VTEP IP: %pI4\n", &zevpn->local_vtep_ip); vty_out(vty, " Mcast group: %pI4\n", @@ -142,6 +146,12 @@ void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt) json_object_string_add(json, "vxlanInterface", zevpn->vxlan_if->name); json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex); + if (zevpn->svi_if) { + json_object_string_add(json, "sviInterface", + zevpn->svi_if->name); + json_object_int_add(json, "sviIfindex", + zevpn->svi_if->ifindex); + } json_object_string_add(json, "vtepIp", inet_ntop(AF_INET, &zevpn->local_vtep_ip, buf, sizeof(buf))); @@ -1048,6 +1058,8 @@ int zebra_evpn_del(zebra_evpn_t *zevpn) zvrf = zebra_vrf_get_evpn(); assert(zvrf); + zevpn->svi_if = NULL; + /* Free the neighbor hash table. */ hash_free(zevpn->neigh_table); zevpn->neigh_table = NULL; @@ -1075,6 +1087,7 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) { struct zserv *client; struct stream *s; + ifindex_t svi_index; int rc; client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); @@ -1082,6 +1095,8 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) if (!client) return 0; + svi_index = zevpn->svi_if ? zevpn->svi_if->ifindex : 0; + s = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id()); @@ -1089,15 +1104,18 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) stream_put_in_addr(s, &zevpn->local_vtep_ip); stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ stream_put_in_addr(s, &zevpn->mcast_grp); + stream_put(s, &svi_index, sizeof(ifindex_t)); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send EVPN_ADD %u %pI4 tenant vrf %s to %s", zevpn->vni, - &zevpn->local_vtep_ip, - vrf_id_to_name(zevpn->vrf_id), - zebra_route_string(client->proto)); + zlog_debug( + "Send EVPN_ADD %u %pI4 tenant vrf %s(%u) SVI index %u to %s", + zevpn->vni, &zevpn->local_vtep_ip, + vrf_id_to_name(zevpn->vrf_id), zevpn->vrf_id, + (zevpn->svi_if ? zevpn->svi_if->ifindex : 0), + zebra_route_string(client->proto)); client->vniadd_cnt++; rc = zserv_send_message(client, s); diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h index 27392ec85..ee9e1406e 100644 --- a/zebra/zebra_evpn.h +++ b/zebra/zebra_evpn.h @@ -98,6 +98,9 @@ struct zebra_evpn_t_ { /* Corresponding VxLAN interface. */ struct interface *vxlan_if; + /* Corresponding SVI interface. */ + struct interface *svi_if; + /* List of remote VTEPs */ zebra_vtep_t *vteps; diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index cebd57636..efbd078a5 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -42,6 +42,7 @@ #include "zebra/zebra_fpm_private.h" #include "zebra/zebra_vxlan_private.h" +#include "zebra/interface.h" /* * af_addr_size @@ -164,7 +165,10 @@ static int netlink_route_info_add_nh(struct netlink_route_info *ri, { struct netlink_nh_info nhi; union g_addr *src; - zebra_l3vni_t *zl3vni = NULL; + struct zebra_vrf *zvrf = NULL; + struct interface *ifp = NULL, *link_if = NULL; + struct zebra_if *zif = NULL; + vni_t vni = 0; memset(&nhi, 0, sizeof(nhi)); src = NULL; @@ -199,12 +203,29 @@ static int netlink_route_info_add_nh(struct netlink_route_info *ri, if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) { nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN; - zl3vni = zl3vni_from_vrf(nexthop->vrf_id); - if (zl3vni && is_l3vni_oper_up(zl3vni)) { - - /* Add VNI to VxLAN encap info */ - nhi.encap_info.vxlan_encap.vni = zl3vni->vni; + /* Extract VNI id for the nexthop SVI interface */ + zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); + if (zvrf) { + ifp = if_lookup_by_index_per_ns(zvrf->zns, + nexthop->ifindex); + if (ifp) { + zif = (struct zebra_if *)ifp->info; + if (zif) { + if (IS_ZEBRA_IF_BRIDGE(ifp)) + link_if = ifp; + else if (IS_ZEBRA_IF_VLAN(ifp)) + link_if = + if_lookup_by_index_per_ns( + zvrf->zns, + zif->link_ifindex); + if (link_if) + vni = vni_id_from_svi(ifp, + link_if); + } + } } + + nhi.encap_info.vxlan_encap.vni = vni; } /* diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 09eb78917..2f3ea7475 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -1012,6 +1012,7 @@ static int zevpn_build_hash_table_zns(struct ns *ns, vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { + zevpn->svi_if = vlan_if; zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf( vlan_if->vrf_id); @@ -1841,6 +1842,27 @@ static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp, return zl3vni; } +vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if) +{ + vni_t vni = 0; + zebra_evpn_t *zevpn = NULL; + zebra_l3vni_t *zl3vni = NULL; + + /* Check if an L3VNI belongs to this SVI interface. + * If not, check if an L2VNI belongs to this SVI interface. + */ + zl3vni = zl3vni_from_svi(ifp, br_if); + if (zl3vni) + vni = zl3vni->vni; + else { + zevpn = zebra_evpn_from_svi(ifp, br_if); + if (zevpn) + vni = zevpn->vni; + } + + return vni; +} + static inline void zl3vni_get_vrr_rmac(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) { @@ -4527,6 +4549,7 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) zevpn = zebra_evpn_from_svi(ifp, link_if); if (zevpn) { + zevpn->svi_if = NULL; zevpn->vrf_id = VRF_DEFAULT; /* update the tenant vrf in BGP */ @@ -4582,6 +4605,7 @@ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) vrf_id_to_name(ifp->vrf_id)); /* update the vrf information for l2-vni and inform bgp */ + zevpn->svi_if = ifp; zevpn->vrf_id = ifp->vrf_id; if (if_is_operative(zevpn->vxlan_if)) @@ -4792,6 +4816,7 @@ int zebra_vxlan_if_up(struct interface *ifp) vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { + zevpn->svi_if = vlan_if; zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) @@ -4894,6 +4919,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) struct zebra_l2info_vxlan *vxl = NULL; zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; + struct interface *vlan_if = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4983,6 +5009,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { /* Delete from client, remove all remote VTEPs */ /* Also, free up all MACs and neighbors. */ + zevpn->svi_if = NULL; zebra_evpn_send_del_to_client(zevpn); zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); @@ -5012,6 +5039,11 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) zebra_evpn_es_set_base_evpn(zevpn); } zevpn_vxlan_if_set(zevpn, ifp, true /* set */); + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) + zevpn->svi_if = vlan_if; + /* Take further actions needed. * Note that if we are here, there is a change of interest. */ @@ -5131,6 +5163,7 @@ int zebra_vxlan_if_add(struct interface *ifp) vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { + zevpn->svi_if = vlan_if; zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 0556c4adc..84ac76b3b 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -224,6 +224,7 @@ extern struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni); extern struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni); extern struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni); extern zebra_l3vni_t *zl3vni_lookup(vni_t vni); +extern vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if); DECLARE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni, bool delete, const char *reason), (rmac, zl3vni, delete, reason)); |