Merge pull request #6695 from adharkar/frr-master-gateway_ip

EVPN route type-5 gateway IP overlay Index
This commit is contained in:
Patrick Ruddy 2021-06-23 09:23:54 +01:00 committed by GitHub
commit fa855f8fa3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 3954 additions and 193 deletions

View File

@ -315,3 +315,4 @@ extern bool is_zero_gw_ip(const union gw_addr *gw_ip, const afi_t afi)
return false;
}

View File

@ -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;
};

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

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

View File

@ -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);

View File

@ -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 (afi == AFI_IP) {
/* 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);
} 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);
if ((oly != OVERLAY_INDEX_TYPE_NONE)
&& (oly != OVERLAY_INDEX_GATEWAY_IP)) {
vty_out(vty, "%%Unknown overlay-index type specified");
return CMD_WARNING;
}
if (rmap_changed) {
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 {
/*
* 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 {
/*
* 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) || (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");
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 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);

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

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

View File

@ -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");

View File

@ -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");

View File

@ -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,

View File

@ -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 {

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -371,6 +371,20 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy,
}
},
{
.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,
},

View File

@ -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
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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)

View File

@ -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
^^^^^^^^^^^^^^^^

View File

@ -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);

View File

@ -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"));
}
}

View File

@ -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
}
]
}
]
]
}
}

View File

@ -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
}

View File

@ -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
}
]
}
]
]
}
}

View File

@ -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
}
]
}
] } }

View File

@ -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
}
]
}
] } }

View File

@ -0,0 +1,6 @@
{
"vrfName": "vrf-blue",
"routerId": "10.100.0.1",
"defaultLocPrf": 100,
"localAS": 101,
"routes": { "100.0.0.21/32": null } }

View File

@ -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"
}
]
}
] } }

View File

@ -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"
}
]
}
] } }

View File

@ -0,0 +1,6 @@
{
"vrfName": "vrf-blue",
"routerId": "10.100.0.1",
"defaultLocPrf": 100,
"localAS": 101,
"routes": { "100::21/128": null } }

View File

@ -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

View File

@ -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
!

View File

@ -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
}
]
}
]
}

View File

@ -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
}
]
}
]
}

View File

@ -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
}

View File

@ -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
}
]
}
]
}

View File

@ -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
}
]
}
]
}

View File

@ -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
}

View File

@ -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
}
]
}
]
]
}
}

View File

@ -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
}
]
}
]
]
}
}

View File

@ -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
}
]
}
]
]
}
}

View File

@ -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
}
]
}
] } }

View File

@ -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
}
]
}
] } }

View File

@ -0,0 +1,6 @@
{
"vrfName": "vrf-blue",
"routerId": "10.100.0.2",
"defaultLocPrf": 100,
"localAS": 101,
"routes": { "100.0.0.21/32": null } }

View File

@ -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
}
]
}
] } }

View File

@ -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
}
]
}
] } }

View File

@ -0,0 +1,6 @@
{
"vrfName": "vrf-blue",
"routerId": "10.100.0.2",
"defaultLocPrf": 100,
"localAS": 101,
"routes": { "100::21/128": null } }

View File

@ -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

View File

@ -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
!

View File

@ -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
}
]
}
]
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
]
}
]
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -0,0 +1,4 @@
!
int host1-eth0
ip address 50.0.1.11/24
ipv6 address 50:0:1::11/48

View File

@ -0,0 +1 @@
!

View File

@ -0,0 +1,4 @@
!
int host1-eth0
ip address 50.0.1.21/24
ipv6 address 50:0:1::21/48

View File

@ -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))

View File

@ -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

View File

@ -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;
}
}
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
/*

View File

@ -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)

View File

@ -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));