mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-14 10:09:17 +00:00
Merge pull request #6695 from adharkar/frr-master-gateway_ip
EVPN route type-5 gateway IP overlay Index
This commit is contained in:
commit
fa855f8fa3
@ -315,3 +315,4 @@ extern bool is_zero_gw_ip(const union gw_addr *gw_ip, const afi_t afi)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,21 @@ union gw_addr {
|
|||||||
struct in6_addr ipv6;
|
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 {
|
struct bgp_route_evpn {
|
||||||
|
enum overlay_index_type type;
|
||||||
|
esi_t eth_s_id;
|
||||||
union gw_addr gw_ip;
|
union gw_addr gw_ip;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2680,10 +2680,14 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi,
|
|||||||
union prefixconstptr pu,
|
union prefixconstptr pu,
|
||||||
mpls_label_t *label, uint32_t num_labels,
|
mpls_label_t *label, uint32_t num_labels,
|
||||||
int addpath_valid, uint32_t addpath_id,
|
int addpath_valid, uint32_t addpath_id,
|
||||||
|
struct bgp_route_evpn *overlay_index,
|
||||||
char *str, int size)
|
char *str, int size)
|
||||||
{
|
{
|
||||||
char rd_buf[RD_ADDRSTRLEN];
|
char rd_buf[RD_ADDRSTRLEN];
|
||||||
char tag_buf[30];
|
char tag_buf[30];
|
||||||
|
char overlay_index_buf[INET6_ADDRSTRLEN + 14];
|
||||||
|
const struct prefix_evpn *evp;
|
||||||
|
|
||||||
/* ' with addpath ID ' 17
|
/* ' with addpath ID ' 17
|
||||||
* max strlen of uint32 + 10
|
* max strlen of uint32 + 10
|
||||||
* +/- (just in case) + 1
|
* +/- (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",
|
snprintf(pathid_buf, sizeof(pathid_buf), " with addpath ID %u",
|
||||||
addpath_id);
|
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';
|
tag_buf[0] = '\0';
|
||||||
if (bgp_labeled_safi(safi) && num_labels) {
|
if (bgp_labeled_safi(safi) && num_labels) {
|
||||||
|
|
||||||
@ -2720,9 +2741,10 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (prd)
|
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,
|
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) {
|
else if (safi == SAFI_FLOWSPEC) {
|
||||||
char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX];
|
char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX];
|
||||||
const struct prefix_fs *fs = pu.fs;
|
const struct prefix_fs *fs = pu.fs;
|
||||||
|
@ -37,7 +37,8 @@
|
|||||||
#define DUMP_DETAIL 32
|
#define DUMP_DETAIL 32
|
||||||
|
|
||||||
/* RD + Prefix + Path-Id */
|
/* 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_open;
|
||||||
extern int dump_update;
|
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_bestpath(struct bgp_dest *dest);
|
||||||
extern bool bgp_debug_zebra(const struct prefix *p);
|
extern bool bgp_debug_zebra(const struct prefix *p);
|
||||||
|
|
||||||
extern const char *
|
extern const char *bgp_debug_rdpfxpath2str(
|
||||||
bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, const struct prefix_rd *prd,
|
afi_t afi, safi_t safi, const struct prefix_rd *prd,
|
||||||
union prefixconstptr pu, mpls_label_t *label,
|
union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels,
|
||||||
uint32_t num_labels, int addpath_valid,
|
int addpath_valid, uint32_t addpath_id,
|
||||||
uint32_t addpath_id, char *str, int size);
|
struct bgp_route_evpn *overlay_index, char *str, int size);
|
||||||
const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data,
|
const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data,
|
||||||
size_t datalen);
|
size_t datalen);
|
||||||
|
|
||||||
|
637
bgpd/bgp_evpn.c
637
bgpd/bgp_evpn.c
@ -53,6 +53,7 @@
|
|||||||
#include "bgpd/bgp_addpath.h"
|
#include "bgpd/bgp_addpath.h"
|
||||||
#include "bgpd/bgp_mac.h"
|
#include "bgpd/bgp_mac.h"
|
||||||
#include "bgpd/bgp_vty.h"
|
#include "bgpd/bgp_vty.h"
|
||||||
|
#include "bgpd/bgp_nht.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Definitions and external declarations.
|
* Definitions and external declarations.
|
||||||
@ -65,6 +66,28 @@ DEFINE_QOBJ_TYPE(bgp_evpn_es);
|
|||||||
* Static function declarations
|
* Static function declarations
|
||||||
*/
|
*/
|
||||||
static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn);
|
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;
|
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 */
|
/* update evpn type-5 route entry */
|
||||||
static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
|
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;
|
afi_t afi = AFI_L2VPN;
|
||||||
safi_t safi = SAFI_EVPN;
|
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;
|
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 */
|
/* Setup RT and encap extended community */
|
||||||
build_evpn_type5_route_extcomm(bgp_vrf, &attr);
|
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)) {
|
dest = bgp_route_next(dest)) {
|
||||||
for (pi = bgp_dest_get_bgp_path_info(dest);
|
for (pi = bgp_dest_get_bgp_path_info(dest);
|
||||||
(pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) {
|
(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_delete(dest, pi);
|
||||||
bgp_path_info_reap(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 new_pi = false;
|
||||||
bool use_l3nhg = false;
|
bool use_l3nhg = false;
|
||||||
bool is_l3nhg_active = false;
|
bool is_l3nhg_active = false;
|
||||||
|
char buf1[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
memset(pp, 0, sizeof(struct prefix));
|
memset(pp, 0, sizeof(struct prefix));
|
||||||
ip_prefix_from_evpn_prefix(evp, pp);
|
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.
|
* make sure to set the flag for next hop attribute.
|
||||||
*/
|
*/
|
||||||
attr = *parent_pi->attr;
|
attr = *parent_pi->attr;
|
||||||
if (afi == AFI_IP6)
|
if (attr.evpn_overlay.type != OVERLAY_INDEX_GATEWAY_IP) {
|
||||||
evpn_convert_nexthop_to_ipv6(&attr);
|
if (afi == AFI_IP6)
|
||||||
else
|
evpn_convert_nexthop_to_ipv6(&attr);
|
||||||
attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
|
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,
|
bgp_evpn_es_vrf_use_nhg(bgp_vrf, &parent_pi->attr->esi, &use_l3nhg,
|
||||||
&is_l3nhg_active, NULL);
|
&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->attr = attr_new;
|
||||||
pi->uptime = bgp_clock();
|
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 */
|
/* Link path to evpn nexthop */
|
||||||
bgp_evpn_path_nh_add(bgp_vrf, pi);
|
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();
|
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. */
|
/* Perform route selection and update zebra, if required. */
|
||||||
ret = evpn_route_select_install(bgp, vpn, dest);
|
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)
|
if (!pi)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
bgp_evpn_remote_ip_hash_del(vpn, pi);
|
||||||
|
|
||||||
/* Mark entry for deletion */
|
/* Mark entry for deletion */
|
||||||
bgp_path_info_delete(dest, pi);
|
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 */
|
mpls_label_t label; /* holds the VNI as in the packet */
|
||||||
int ret;
|
int ret;
|
||||||
afi_t gw_afi;
|
afi_t gw_afi;
|
||||||
bool is_valid_update = false;
|
bool is_valid_update = true;
|
||||||
|
|
||||||
/* Type-5 route should be 34 or 58 bytes:
|
/* Type-5 route should be 34 or 58 bytes:
|
||||||
* RD (8), ESI (10), Eth Tag (4), IP len (1), IP (4 or 16),
|
* 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 */
|
/* Additional information outside of prefix - ESI and GW IP */
|
||||||
memset(&evpn, 0, sizeof(evpn));
|
memset(&evpn, 0, sizeof(evpn));
|
||||||
|
|
||||||
/* Fetch ESI */
|
/* Fetch ESI overlay index */
|
||||||
if (attr)
|
if (attr)
|
||||||
memcpy(&attr->esi, pfx, sizeof(esi_t));
|
memcpy(&evpn.eth_s_id, pfx, sizeof(esi_t));
|
||||||
pfx += ESI_BYTES;
|
pfx += ESI_BYTES;
|
||||||
|
|
||||||
/* Fetch Ethernet Tag. */
|
/* Fetch Ethernet Tag. */
|
||||||
@ -4031,25 +4127,53 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
|
|||||||
* field
|
* 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) {
|
if (attr) {
|
||||||
is_valid_update = true;
|
if (is_zero_mac(&attr->rmac)
|
||||||
if (is_zero_mac(&attr->rmac) &&
|
&& !bgp_evpn_is_esi_valid(&evpn.eth_s_id)
|
||||||
is_zero_gw_ip(&evpn.gw_ip, gw_afi))
|
&& 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;
|
is_valid_update = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_mcast_mac(&attr->rmac) || is_bcast_mac(&attr->rmac))
|
if (is_mcast_mac(&attr->rmac) || is_bcast_mac(&attr->rmac))
|
||||||
is_valid_update = false;
|
is_valid_update = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process the route. */
|
/* Process the route. */
|
||||||
if (is_valid_update)
|
if (attr && is_valid_update)
|
||||||
ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
|
ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
|
||||||
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
|
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
|
||||||
&prd, &label, 1, 0, &evpn);
|
&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,
|
ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
|
||||||
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
|
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
|
||||||
&prd, &label, 1, &evpn);
|
&prd, &label, 1, &evpn);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
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 */
|
/* Prefix contains RD, ESI, EthTag, IP length, IP, GWIP and VNI */
|
||||||
stream_putc(s, 8 + 10 + 4 + 1 + len + 3);
|
stream_putc(s, 8 + 10 + 4 + 1 + len + 3);
|
||||||
stream_put(s, prd->val, 8);
|
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));
|
stream_put(s, &attr->esi, sizeof(esi_t));
|
||||||
else
|
else
|
||||||
stream_put(s, 0, sizeof(esi_t));
|
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);
|
stream_put_ipv4(s, p_evpn_p->prefix_addr.ip.ipaddr_v4.s_addr);
|
||||||
else
|
else
|
||||||
stream_put(s, &p_evpn_p->prefix_addr.ip.ipaddr_v6, 16);
|
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 =
|
const struct bgp_route_evpn *evpn_overlay =
|
||||||
bgp_attr_get_evpn_overlay(attr);
|
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;
|
struct prefix_evpn evp;
|
||||||
|
|
||||||
build_type5_prefix_from_ip_prefix(&evp, p);
|
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)
|
if (ret)
|
||||||
flog_err(EC_BGP_EVPN_ROUTE_CREATE,
|
flog_err(EC_BGP_EVPN_ROUTE_CREATE,
|
||||||
"%u: Failed to create type-5 route for prefix %pFX",
|
"%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 bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
|
||||||
struct in_addr originator_ip,
|
struct in_addr originator_ip,
|
||||||
vrf_id_t tenant_vrf_id,
|
vrf_id_t tenant_vrf_id,
|
||||||
struct in_addr mcast_grp)
|
struct in_addr mcast_grp,
|
||||||
|
ifindex_t svi_ifindex)
|
||||||
{
|
{
|
||||||
struct bgpevpn *vpn;
|
struct bgpevpn *vpn;
|
||||||
|
|
||||||
@ -5148,6 +5273,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
|
|||||||
vpn->originator_ip = originator_ip;
|
vpn->originator_ip = originator_ip;
|
||||||
vpn->tenant_vrf_id = tenant_vrf_id;
|
vpn->tenant_vrf_id = tenant_vrf_id;
|
||||||
vpn->mcast_grp = mcast_grp;
|
vpn->mcast_grp = mcast_grp;
|
||||||
|
vpn->svi_ifindex = svi_ifindex;
|
||||||
|
|
||||||
/* Initialize route-target import and export lists */
|
/* Initialize route-target import and export lists */
|
||||||
vpn->import_rtl = list_new();
|
vpn->import_rtl = list_new();
|
||||||
@ -5168,6 +5294,9 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
|
|||||||
return NULL;
|
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 */
|
/* add to l2vni list on corresponding vrf */
|
||||||
bgpevpn_link_to_l3vni(vpn);
|
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)
|
void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
|
||||||
{
|
{
|
||||||
|
bgp_evpn_remote_ip_hash_destroy(vpn);
|
||||||
bgp_evpn_vni_es_cleanup(vpn);
|
bgp_evpn_vni_es_cleanup(vpn);
|
||||||
bgpevpn_unlink_from_l3vni(vpn);
|
bgpevpn_unlink_from_l3vni(vpn);
|
||||||
bgp_table_unlock(vpn->route_table);
|
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->import_rtl);
|
||||||
list_delete(&vpn->export_rtl);
|
list_delete(&vpn->export_rtl);
|
||||||
bf_release_index(bm->rd_idspace, vpn->rd_id);
|
bf_release_index(bm->rd_idspace, vpn->rd_id);
|
||||||
|
hash_release(bgp->vni_svi_hash, vpn);
|
||||||
hash_release(bgp->vnihash, vpn);
|
hash_release(bgp->vnihash, vpn);
|
||||||
QOBJ_UNREG(vpn);
|
QOBJ_UNREG(vpn);
|
||||||
XFREE(MTYPE_BGP_EVPN, 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);
|
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
|
* 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,
|
int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
|
||||||
struct in_addr originator_ip,
|
struct in_addr originator_ip,
|
||||||
vrf_id_t tenant_vrf_id,
|
vrf_id_t tenant_vrf_id,
|
||||||
struct in_addr mcast_grp)
|
struct in_addr mcast_grp,
|
||||||
|
ifindex_t svi_ifindex)
|
||||||
{
|
{
|
||||||
struct bgpevpn *vpn;
|
struct bgpevpn *vpn;
|
||||||
struct prefix_evpn p;
|
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)
|
if (is_vni_live(vpn)
|
||||||
&& IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)
|
&& IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)
|
||||||
&& IPV4_ADDR_SAME(&vpn->mcast_grp, &mcast_grp)
|
&& 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
|
/* Probably some other param has changed that we don't
|
||||||
* care about. */
|
* care about. */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bgp_evpn_mcast_grp_change(bgp, vpn, mcast_grp);
|
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. */
|
/* Update tenant_vrf_id if it has changed. */
|
||||||
if (vpn->tenant_vrf_id != tenant_vrf_id) {
|
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);
|
bgpevpn_unlink_from_l3vni(vpn);
|
||||||
vpn->tenant_vrf_id = tenant_vrf_id;
|
vpn->tenant_vrf_id = tenant_vrf_id;
|
||||||
bgpevpn_link_to_l3vni(vpn);
|
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
|
/* 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. */
|
/* Create or update as appropriate. */
|
||||||
if (!vpn) {
|
if (!vpn) {
|
||||||
vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id,
|
vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id,
|
||||||
mcast_grp);
|
mcast_grp, svi_ifindex);
|
||||||
if (!vpn) {
|
if (!vpn) {
|
||||||
flog_err(
|
flog_err(
|
||||||
EC_BGP_VNI,
|
EC_BGP_VNI,
|
||||||
@ -5768,6 +5949,8 @@ void bgp_evpn_cleanup(struct bgp *bgp)
|
|||||||
hash_free(bgp->vrf_import_rt_hash);
|
hash_free(bgp->vrf_import_rt_hash);
|
||||||
bgp->vrf_import_rt_hash = NULL;
|
bgp->vrf_import_rt_hash = NULL;
|
||||||
|
|
||||||
|
hash_free(bgp->vni_svi_hash);
|
||||||
|
bgp->vni_svi_hash = NULL;
|
||||||
hash_free(bgp->vnihash);
|
hash_free(bgp->vnihash);
|
||||||
bgp->vnihash = NULL;
|
bgp->vnihash = NULL;
|
||||||
|
|
||||||
@ -5786,6 +5969,9 @@ void bgp_evpn_init(struct bgp *bgp)
|
|||||||
{
|
{
|
||||||
bgp->vnihash =
|
bgp->vnihash =
|
||||||
hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash");
|
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 =
|
bgp->import_rt_hash =
|
||||||
hash_create(import_rt_hash_key_make, import_rt_hash_cmp,
|
hash_create(import_rt_hash_key_make, import_rt_hash_cmp,
|
||||||
"BGP Import RT Hash");
|
"BGP Import RT Hash");
|
||||||
@ -5878,3 +6064,408 @@ bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx)
|
|||||||
|
|
||||||
return false;
|
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);
|
||||||
|
}
|
||||||
|
@ -63,14 +63,18 @@ static inline int advertise_type5_routes(struct bgp *bgp_vrf,
|
|||||||
if (!bgp_vrf->l3vni)
|
if (!bgp_vrf->l3vni)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (afi == AFI_IP &&
|
if ((afi == AFI_IP)
|
||||||
CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
|
&& ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
|
||||||
BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST))
|
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;
|
return 1;
|
||||||
|
|
||||||
if (afi == AFI_IP6 &&
|
if ((afi == AFI_IP6)
|
||||||
CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
|
&& ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
|
||||||
BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))
|
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 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -152,6 +156,14 @@ static inline bool is_route_injectable_into_evpn(struct bgp_path_info *pi)
|
|||||||
return true;
|
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,
|
extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf,
|
||||||
const struct prefix *p,
|
const struct prefix *p,
|
||||||
struct attr *src_attr, afi_t afi,
|
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,
|
extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
|
||||||
struct in_addr originator_ip,
|
struct in_addr originator_ip,
|
||||||
vrf_id_t tenant_vrf_id,
|
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_flood_control_change(struct bgp *bgp);
|
||||||
extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp);
|
extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp);
|
||||||
extern void bgp_evpn_cleanup(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 int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx);
|
||||||
extern bool bgp_evpn_is_prefix_nht_supported(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 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 */
|
#endif /* _QUAGGA_BGP_EVPN_H */
|
||||||
|
@ -62,6 +62,7 @@ RB_PROTOTYPE(bgp_es_evi_rb_head, bgp_evpn_es_evi, rb_node,
|
|||||||
struct bgpevpn {
|
struct bgpevpn {
|
||||||
vni_t vni;
|
vni_t vni;
|
||||||
vrf_id_t tenant_vrf_id;
|
vrf_id_t tenant_vrf_id;
|
||||||
|
ifindex_t svi_ifindex;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
#define VNI_FLAG_CFGD 0x1 /* VNI is user configured */
|
#define VNI_FLAG_CFGD 0x1 /* VNI is user configured */
|
||||||
#define VNI_FLAG_LIVE 0x2 /* VNI is "live" */
|
#define VNI_FLAG_LIVE 0x2 /* VNI is "live" */
|
||||||
@ -102,6 +103,15 @@ struct bgpevpn {
|
|||||||
struct list *import_rtl;
|
struct list *import_rtl;
|
||||||
struct list *export_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
|
/* Route table for EVPN routes for
|
||||||
* this VNI. */
|
* this VNI. */
|
||||||
struct bgp_table *route_table;
|
struct bgp_table *route_table;
|
||||||
@ -178,6 +188,12 @@ struct bgp_evpn_info {
|
|||||||
bool is_anycast_mac;
|
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)
|
static inline int is_vrf_rd_configured(struct bgp *bgp_vrf)
|
||||||
{
|
{
|
||||||
return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD));
|
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,
|
extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
|
||||||
struct in_addr originator_ip,
|
struct in_addr originator_ip,
|
||||||
vrf_id_t tenant_vrf_id,
|
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 void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn);
|
||||||
extern bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni);
|
extern bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni);
|
||||||
extern int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn);
|
extern int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn);
|
||||||
|
@ -59,6 +59,17 @@ struct vni_walk_ctx {
|
|||||||
int detail;
|
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,
|
static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt,
|
||||||
json_object *json)
|
json_object *json)
|
||||||
{
|
{
|
||||||
@ -520,6 +531,9 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
|
|||||||
else
|
else
|
||||||
json_object_string_add(json, "advertiseSviMacIp",
|
json_object_string_add(json, "advertiseSviMacIp",
|
||||||
"Disabled");
|
"Disabled");
|
||||||
|
json_object_string_add(
|
||||||
|
json, "sviInterface",
|
||||||
|
ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id));
|
||||||
} else {
|
} else {
|
||||||
vty_out(vty, "VNI: %d", vpn->vni);
|
vty_out(vty, "VNI: %d", vpn->vni);
|
||||||
if (is_vni_live(vpn))
|
if (is_vni_live(vpn))
|
||||||
@ -553,6 +567,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
|
|||||||
else
|
else
|
||||||
vty_out(vty, " Advertise-svi-macip : %s\n",
|
vty_out(vty, " Advertise-svi-macip : %s\n",
|
||||||
"Disabled");
|
"Disabled");
|
||||||
|
vty_out(vty, " SVI interface : %s\n",
|
||||||
|
ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!json)
|
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
|
/* tenant vrf will be updated when we get local_vni_add from
|
||||||
* zebra
|
* 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) {
|
if (!vpn) {
|
||||||
flog_err(
|
flog_err(
|
||||||
EC_BGP_VNI,
|
EC_BGP_VNI,
|
||||||
@ -3286,6 +3302,28 @@ static void evpn_unset_advertise_all_vni(struct bgp *bgp)
|
|||||||
bgp_evpn_cleanup_on_disable(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
|
* EVPN - use RFC8365 to auto-derive RT
|
||||||
*/
|
*/
|
||||||
@ -3784,10 +3822,11 @@ DEFUN_HIDDEN (no_bgp_evpn_advertise_vni_subnet,
|
|||||||
|
|
||||||
DEFUN (bgp_evpn_advertise_type5,
|
DEFUN (bgp_evpn_advertise_type5,
|
||||||
bgp_evpn_advertise_type5_cmd,
|
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"
|
"Advertise prefix routes\n"
|
||||||
BGP_AFI_HELP_STR
|
BGP_AFI_HELP_STR
|
||||||
BGP_SAFI_HELP_STR
|
BGP_SAFI_HELP_STR
|
||||||
|
"advertise gateway IP overlay index\n"
|
||||||
"route-map for filtering specific routes\n"
|
"route-map for filtering specific routes\n"
|
||||||
"Name of the route map\n")
|
"Name of the route map\n")
|
||||||
{
|
{
|
||||||
@ -3799,9 +3838,14 @@ DEFUN (bgp_evpn_advertise_type5,
|
|||||||
safi_t safi = 0;
|
safi_t safi = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int rmap_changed = 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_afi(argv, argc, &idx_afi, &afi);
|
||||||
argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
|
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);
|
ret = argv_find(argv, argc, "route-map", &idx_rmap);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (!bgp_vrf->adv_cmd_rmap[afi][safi].name)
|
if (!bgp_vrf->adv_cmd_rmap[afi][safi].name)
|
||||||
@ -3826,39 +3870,149 @@ DEFUN (bgp_evpn_advertise_type5,
|
|||||||
return CMD_WARNING;
|
return CMD_WARNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (afi == AFI_IP) {
|
if ((oly != OVERLAY_INDEX_TYPE_NONE)
|
||||||
|
&& (oly != OVERLAY_INDEX_GATEWAY_IP)) {
|
||||||
/* if we are already advertising ipv4 prefix as type-5
|
vty_out(vty, "%%Unknown overlay-index type specified");
|
||||||
* nothing to do
|
return CMD_WARNING;
|
||||||
*/
|
|
||||||
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 (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);
|
bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
|
||||||
if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
|
if (rmap_changed) {
|
||||||
XFREE(MTYPE_ROUTE_MAP_NAME,
|
if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
|
||||||
bgp_vrf->adv_cmd_rmap[afi][safi].name);
|
XFREE(MTYPE_ROUTE_MAP_NAME,
|
||||||
route_map_counter_decrement(
|
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].map);
|
||||||
bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
|
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].map = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3912,22 +4066,30 @@ DEFUN (no_bgp_evpn_advertise_type5,
|
|||||||
/* if we are not advertising ipv4 prefix as type-5
|
/* if we are not advertising ipv4 prefix as type-5
|
||||||
* nothing to do
|
* nothing to do
|
||||||
*/
|
*/
|
||||||
if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
|
if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
|
||||||
BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) {
|
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);
|
bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
|
||||||
UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
|
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 {
|
} else {
|
||||||
|
|
||||||
/* if we are not advertising ipv6 prefix as type-5
|
/* if we are not advertising ipv6 prefix as type-5
|
||||||
* nothing to do
|
* nothing to do
|
||||||
*/
|
*/
|
||||||
if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
|
if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
|
||||||
BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) {
|
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);
|
bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
|
||||||
UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
|
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;
|
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,
|
DEFPY (bgp_evpn_advertise_pip_ip_mac,
|
||||||
bgp_evpn_advertise_pip_ip_mac_cmd,
|
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>]]",
|
"[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;
|
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,
|
DEFPY(show_bgp_l2vpn_evpn_es_evi,
|
||||||
show_bgp_l2vpn_evpn_es_evi_cmd,
|
show_bgp_l2vpn_evpn_es_evi_cmd,
|
||||||
"show bgp l2vpn evpn es-evi [vni (1-16777215)$vni] [json$uj] [detail$detail]",
|
"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)
|
if (bgp->evpn_info->advertise_svi_macip)
|
||||||
vty_out(vty, " advertise-svi-ip\n");
|
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 !=
|
if (bgp_mh_info->host_routes_use_l3nhg !=
|
||||||
BGP_EVPN_MH_USE_ES_L3NHG_DEF) {
|
BGP_EVPN_MH_USE_ES_L3NHG_DEF) {
|
||||||
if (bgp_mh_info->host_routes_use_l3nhg)
|
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");
|
vty_out(vty, " flooding disable\n");
|
||||||
|
|
||||||
if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
|
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)
|
if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name)
|
||||||
vty_out(vty, " advertise ipv4 unicast route-map %s\n",
|
vty_out(vty, " advertise ipv4 unicast route-map %s\n",
|
||||||
bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name);
|
bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name);
|
||||||
else
|
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],
|
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)
|
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);
|
bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name);
|
||||||
else
|
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],
|
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_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_rx_disable_cmd);
|
||||||
install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_tx_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 */
|
/* test commands */
|
||||||
install_element(BGP_EVPN_NODE, &test_es_add_cmd);
|
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_es_vrf_cmd);
|
||||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_nh_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_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_summary_cmd);
|
||||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd);
|
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd);
|
||||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
|
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
|
||||||
|
@ -28,6 +28,10 @@ extern void bgp_ethernetvpn_init(void);
|
|||||||
#define L2VPN_HELP_STR "Layer 2 Virtual Private Network\n"
|
#define L2VPN_HELP_STR "Layer 2 Virtual Private Network\n"
|
||||||
#define EVPN_HELP_STR "Ethernet 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 */
|
/* Parse type from "type <ead|1|...>", return -1 on failure */
|
||||||
extern int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv,
|
extern int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv,
|
||||||
int argc);
|
int argc);
|
||||||
|
@ -200,8 +200,8 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer,
|
|||||||
AFI_L2VPN, SAFI_EVPN, &prd,
|
AFI_L2VPN, SAFI_EVPN, &prd,
|
||||||
p, label_pnt, num_labels,
|
p, label_pnt, num_labels,
|
||||||
pi->addpath_rx_id ? 1 : 0,
|
pi->addpath_rx_id ? 1 : 0,
|
||||||
pi->addpath_rx_id, pfx_buf,
|
pi->addpath_rx_id, NULL,
|
||||||
sizeof(pfx_buf));
|
pfx_buf, sizeof(pfx_buf));
|
||||||
zlog_debug(
|
zlog_debug(
|
||||||
"%s skip update of %s marked as removed",
|
"%s skip update of %s marked as removed",
|
||||||
peer->host, pfx_buf);
|
peer->host, pfx_buf);
|
||||||
|
@ -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_VPN, "BGP prefix-sid srv6 vpn service");
|
||||||
DEFINE_MTYPE(BGPD, BGP_SRV6_SID, "BGP srv6 segment-id");
|
DEFINE_MTYPE(BGPD, BGP_SRV6_SID, "BGP srv6 segment-id");
|
||||||
DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function");
|
DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function");
|
||||||
|
DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry");
|
||||||
|
@ -142,4 +142,6 @@ DECLARE_MTYPE(BGP_SRV6_VPN);
|
|||||||
DECLARE_MTYPE(BGP_SRV6_SID);
|
DECLARE_MTYPE(BGP_SRV6_SID);
|
||||||
DECLARE_MTYPE(BGP_SRV6_FUNCTION);
|
DECLARE_MTYPE(BGP_SRV6_FUNCTION);
|
||||||
|
|
||||||
|
DECLARE_MTYPE(EVPN_REMOTE_IP);
|
||||||
|
|
||||||
#endif /* _QUAGGA_BGP_MEMORY_H */
|
#endif /* _QUAGGA_BGP_MEMORY_H */
|
||||||
|
@ -182,24 +182,52 @@ int bgp_router_destroy(struct nb_cb_destroy_args *args)
|
|||||||
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) {
|
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) {
|
||||||
if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
|
if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
|
||||||
continue;
|
continue;
|
||||||
if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
|
if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP]
|
||||||
BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
|
[SAFI_UNICAST],
|
||||||
CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
|
BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)
|
||||||
BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
|
|| CHECK_FLAG(
|
||||||
CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
|
tmp_bgp->af_flags[AFI_IP6]
|
||||||
BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
|
[SAFI_UNICAST],
|
||||||
CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
|
BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)
|
||||||
BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
|
|| CHECK_FLAG(
|
||||||
CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
|
tmp_bgp->af_flags[AFI_IP]
|
||||||
BGP_CONFIG_VRF_TO_VRF_EXPORT) ||
|
[SAFI_UNICAST],
|
||||||
CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
|
BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)
|
||||||
BGP_CONFIG_VRF_TO_VRF_EXPORT) ||
|
|| CHECK_FLAG(
|
||||||
(bgp == bgp_get_evpn() &&
|
tmp_bgp->af_flags[AFI_IP6]
|
||||||
(CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
|
[SAFI_UNICAST],
|
||||||
BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST) ||
|
BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)
|
||||||
CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
|
|| CHECK_FLAG(
|
||||||
BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))) ||
|
tmp_bgp->af_flags[AFI_IP]
|
||||||
(tmp_bgp->vnihash && hashcount(tmp_bgp->vnihash))) {
|
[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(
|
snprintf(
|
||||||
args->errmsg, args->errmsg_len,
|
args->errmsg, args->errmsg_len,
|
||||||
"Cannot delete default BGP instance. Dependent VRF instances exist\n");
|
"Cannot delete default BGP instance. Dependent VRF instances exist\n");
|
||||||
|
@ -835,6 +835,18 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
|
|||||||
bnc->metric, bnc->path_count);
|
bnc->metric, bnc->path_count);
|
||||||
if (peer)
|
if (peer)
|
||||||
vty_out(vty, ", peer %s", peer->host);
|
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");
|
vty_out(vty, "\n");
|
||||||
bgp_show_nexthops_detail(vty, bgp, bnc);
|
bgp_show_nexthops_detail(vty, bgp, bnc);
|
||||||
} else {
|
} else {
|
||||||
@ -844,6 +856,8 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
|
|||||||
bnc->path_count);
|
bnc->path_count);
|
||||||
if (peer)
|
if (peer)
|
||||||
vty_out(vty, ", peer %s", peer->host);
|
vty_out(vty, ", peer %s", peer->host);
|
||||||
|
if (bnc->is_evpn_gwip_nexthop)
|
||||||
|
vty_out(vty, " EVPN Gateway IP");
|
||||||
vty_out(vty, "\n");
|
vty_out(vty, "\n");
|
||||||
if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED))
|
if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED))
|
||||||
vty_out(vty, " Must be Connected\n");
|
vty_out(vty, " Must be Connected\n");
|
||||||
|
@ -56,6 +56,10 @@ struct bgp_nexthop_cache {
|
|||||||
time_t last_update;
|
time_t last_update;
|
||||||
uint16_t flags;
|
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_VALID (1 << 0)
|
||||||
#define BGP_NEXTHOP_REGISTERED (1 << 1)
|
#define BGP_NEXTHOP_REGISTERED (1 << 1)
|
||||||
#define BGP_NEXTHOP_CONNECTED (1 << 2)
|
#define BGP_NEXTHOP_CONNECTED (1 << 2)
|
||||||
@ -64,11 +68,29 @@ struct bgp_nexthop_cache {
|
|||||||
#define BGP_STATIC_ROUTE_EXACT_MATCH (1 << 5)
|
#define BGP_STATIC_ROUTE_EXACT_MATCH (1 << 5)
|
||||||
#define BGP_NEXTHOP_LABELED_VALID (1 << 6)
|
#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;
|
uint16_t change_flags;
|
||||||
|
|
||||||
#define BGP_NEXTHOP_CHANGED (1 << 0)
|
#define BGP_NEXTHOP_CHANGED (1 << 0)
|
||||||
#define BGP_NEXTHOP_METRIC_CHANGED (1 << 1)
|
#define BGP_NEXTHOP_METRIC_CHANGED (1 << 1)
|
||||||
#define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2)
|
#define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2)
|
||||||
|
#define BGP_NEXTHOP_MACIP_CHANGED (1 << 3)
|
||||||
|
|
||||||
/* Back pointer to the cache tree this entry belongs to. */
|
/* Back pointer to the cache tree this entry belongs to. */
|
||||||
struct bgp_nexthop_cache_head *tree;
|
struct bgp_nexthop_cache_head *tree;
|
||||||
@ -79,6 +101,11 @@ struct bgp_nexthop_cache {
|
|||||||
LIST_HEAD(path_list, bgp_path_info) paths;
|
LIST_HEAD(path_list, bgp_path_info) paths;
|
||||||
unsigned int path_count;
|
unsigned int path_count;
|
||||||
struct bgp *bgp;
|
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,
|
extern int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a,
|
||||||
|
@ -53,7 +53,6 @@ static void register_zebra_rnh(struct bgp_nexthop_cache *bnc,
|
|||||||
int is_bgp_static_route);
|
int is_bgp_static_route);
|
||||||
static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc,
|
static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc,
|
||||||
int is_bgp_static_route);
|
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 make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p);
|
||||||
static int bgp_nht_ifp_initial(struct thread *thread);
|
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) {
|
if (is_bgp_static_route) {
|
||||||
SET_FLAG(bnc->flags, 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;
|
return 1;
|
||||||
else if (safi == SAFI_UNICAST && pi
|
else if (safi == SAFI_UNICAST && pi
|
||||||
&& pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra
|
&& 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);
|
return bgp_isvalid_labeled_nexthop(bnc);
|
||||||
} else
|
} else
|
||||||
return (bgp_isvalid_nexthop(bnc));
|
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_head = NULL;
|
||||||
struct nexthop *nhlist_tail = NULL;
|
struct nexthop *nhlist_tail = NULL;
|
||||||
int i;
|
int i;
|
||||||
|
bool evpn_resolved = false;
|
||||||
|
|
||||||
bnc->last_update = bgp_clock();
|
bnc->last_update = bgp_clock();
|
||||||
bnc->change_flags = 0;
|
bnc->change_flags = 0;
|
||||||
@ -417,7 +420,8 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc,
|
|||||||
if (!bnc->nexthop_num)
|
if (!bnc->nexthop_num)
|
||||||
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
|
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->metric = nhr->metric;
|
||||||
bnc->nexthop_num = nhr->nexthop_num;
|
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_free(bnc);
|
||||||
bnc->nexthop = nhlist_head;
|
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 {
|
} else {
|
||||||
|
bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE;
|
||||||
bnc->flags &= ~BGP_NEXTHOP_VALID;
|
bnc->flags &= ~BGP_NEXTHOP_VALID;
|
||||||
bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID;
|
bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID;
|
||||||
bnc->nexthop_num = nhr->nexthop_num;
|
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_VALID);
|
||||||
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
|
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
|
||||||
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
|
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:
|
* RETURNS:
|
||||||
* void.
|
* void.
|
||||||
*/
|
*/
|
||||||
static void evaluate_paths(struct bgp_nexthop_cache *bnc)
|
void evaluate_paths(struct bgp_nexthop_cache *bnc)
|
||||||
{
|
{
|
||||||
struct bgp_dest *dest;
|
struct bgp_dest *dest;
|
||||||
struct bgp_path_info *path;
|
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
|
* In case of unicast routes that were imported from vpn
|
||||||
* and that have labels, they are valid only if there are
|
* and that have labels, they are valid only if there are
|
||||||
* nexthops with labels
|
* nexthops with labels
|
||||||
|
*
|
||||||
|
* If the nexthop is EVPN gateway-IP,
|
||||||
|
* do not check for a valid label.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool bnc_is_valid_nexthop = false;
|
bool bnc_is_valid_nexthop = false;
|
||||||
bool path_valid = false;
|
bool path_valid = false;
|
||||||
|
|
||||||
if (safi == SAFI_UNICAST &&
|
if (safi == SAFI_UNICAST && path->sub_type == BGP_ROUTE_IMPORTED
|
||||||
path->sub_type == BGP_ROUTE_IMPORTED &&
|
&& path->extra && path->extra->num_labels
|
||||||
path->extra &&
|
&& (path->attr->evpn_overlay.type
|
||||||
path->extra->num_labels) {
|
!= OVERLAY_INDEX_GATEWAY_IP)) {
|
||||||
|
|
||||||
bnc_is_valid_nexthop =
|
bnc_is_valid_nexthop =
|
||||||
bgp_isvalid_labeled_nexthop(bnc) ? true : false;
|
bgp_isvalid_labeled_nexthop(bnc) ? true : false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -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_reg_enhe_cap_intfs(struct peer *peer);
|
||||||
extern void bgp_nht_dereg_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 */
|
/* APIs for setting up and allocating L3 nexthop group ids */
|
||||||
extern uint32_t bgp_l3nhg_id_alloc(void);
|
extern uint32_t bgp_l3nhg_id_alloc(void);
|
||||||
|
@ -3451,23 +3451,6 @@ struct bgp_path_info *info_make(int type, int sub_type, unsigned short instance,
|
|||||||
return new;
|
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,
|
static bool overlay_index_equal(afi_t afi, struct bgp_path_info *path,
|
||||||
union gw_addr *gw_ip)
|
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)
|
if (has_valid_label)
|
||||||
assert(label != NULL);
|
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
|
/* When peer's soft reconfiguration enabled. Record input packet in
|
||||||
Adj-RIBs-In. */
|
Adj-RIBs-In. */
|
||||||
if (!soft_reconfig
|
if (!soft_reconfig
|
||||||
@ -3825,12 +3813,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
|||||||
goto filtered;
|
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
|
/* The flag BGP_NODE_FIB_INSTALL_PENDING is for the following
|
||||||
* condition :
|
* condition :
|
||||||
* Suppress fib is enabled
|
* 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
|
&& (!has_valid_label
|
||||||
|| memcmp(&(bgp_path_info_extra_get(pi))->label, label,
|
|| memcmp(&(bgp_path_info_extra_get(pi))->label, label,
|
||||||
num_labels * sizeof(mpls_label_t))
|
num_labels * sizeof(mpls_label_t))
|
||||||
== 0)
|
== 0)) {
|
||||||
&& (overlay_index_equal(
|
|
||||||
afi, pi,
|
|
||||||
evpn == NULL ? NULL : &evpn->gw_ip))) {
|
|
||||||
if (get_active_bdc_from_pi(pi, afi, safi)
|
if (get_active_bdc_from_pi(pi, afi, safi)
|
||||||
&& peer->sort == BGP_PEER_EBGP
|
&& peer->sort == BGP_PEER_EBGP
|
||||||
&& CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) {
|
&& 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(
|
bgp_debug_rdpfxpath2str(
|
||||||
afi, safi, prd, p, label,
|
afi, safi, prd, p, label,
|
||||||
num_labels, addpath_id ? 1 : 0,
|
num_labels, addpath_id ? 1 : 0,
|
||||||
addpath_id, pfx_buf,
|
addpath_id, evpn, pfx_buf,
|
||||||
sizeof(pfx_buf));
|
sizeof(pfx_buf));
|
||||||
zlog_debug("%s rcvd %s", peer->host,
|
zlog_debug("%s rcvd %s", peer->host,
|
||||||
pfx_buf);
|
pfx_buf);
|
||||||
@ -3902,7 +3881,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
|||||||
bgp_debug_rdpfxpath2str(
|
bgp_debug_rdpfxpath2str(
|
||||||
afi, safi, prd, p, label,
|
afi, safi, prd, p, label,
|
||||||
num_labels, addpath_id ? 1 : 0,
|
num_labels, addpath_id ? 1 : 0,
|
||||||
addpath_id, pfx_buf,
|
addpath_id, evpn, pfx_buf,
|
||||||
sizeof(pfx_buf));
|
sizeof(pfx_buf));
|
||||||
zlog_debug(
|
zlog_debug(
|
||||||
"%s rcvd %s...duplicate ignored",
|
"%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)) {
|
if (bgp_debug_update(peer, p, NULL, 1)) {
|
||||||
bgp_debug_rdpfxpath2str(
|
bgp_debug_rdpfxpath2str(
|
||||||
afi, safi, prd, p, label, num_labels,
|
afi, safi, prd, p, label, num_labels,
|
||||||
addpath_id ? 1 : 0, addpath_id, pfx_buf,
|
addpath_id ? 1 : 0, addpath_id, evpn,
|
||||||
sizeof(pfx_buf));
|
pfx_buf, sizeof(pfx_buf));
|
||||||
zlog_debug(
|
zlog_debug(
|
||||||
"%s rcvd %s, flapped quicker than processing",
|
"%s rcvd %s, flapped quicker than processing",
|
||||||
peer->host, pfx_buf);
|
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)) {
|
if (bgp_debug_update(peer, p, NULL, 1)) {
|
||||||
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label,
|
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label,
|
||||||
num_labels, addpath_id ? 1 : 0,
|
num_labels, addpath_id ? 1 : 0,
|
||||||
addpath_id, pfx_buf,
|
addpath_id, evpn, pfx_buf,
|
||||||
sizeof(pfx_buf));
|
sizeof(pfx_buf));
|
||||||
zlog_debug("%s rcvd %s", peer->host, 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,
|
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
|
||||||
addpath_id ? 1 : 0, addpath_id, pfx_buf,
|
addpath_id ? 1 : 0, addpath_id, evpn,
|
||||||
sizeof(pfx_buf));
|
pfx_buf, sizeof(pfx_buf));
|
||||||
zlog_debug("%s rcvd %s", peer->host, 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. */
|
/* Nexthop reachability check. */
|
||||||
if (((afi == AFI_IP || afi == AFI_IP6)
|
if (((afi == AFI_IP || afi == AFI_IP6)
|
||||||
&& (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST))
|
&& (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST))
|
||||||
@ -4362,8 +4336,8 @@ filtered:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
|
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
|
||||||
addpath_id ? 1 : 0, addpath_id, pfx_buf,
|
addpath_id ? 1 : 0, addpath_id, evpn,
|
||||||
sizeof(pfx_buf));
|
pfx_buf, sizeof(pfx_buf));
|
||||||
zlog_debug("%s rcvd UPDATE about %s -- DENIED due to: %s",
|
zlog_debug("%s rcvd UPDATE about %s -- DENIED due to: %s",
|
||||||
peer->host, pfx_buf, reason);
|
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)) {
|
if (bgp_debug_update(peer, p, NULL, 1)) {
|
||||||
bgp_debug_rdpfxpath2str(
|
bgp_debug_rdpfxpath2str(
|
||||||
afi, safi, prd, p, label, num_labels,
|
afi, safi, prd, p, label, num_labels,
|
||||||
addpath_id ? 1 : 0, addpath_id, pfx_buf,
|
addpath_id ? 1 : 0, addpath_id, NULL,
|
||||||
sizeof(pfx_buf));
|
pfx_buf, sizeof(pfx_buf));
|
||||||
zlog_debug(
|
zlog_debug(
|
||||||
"%s withdrawing route %s not in adj-in",
|
"%s withdrawing route %s not in adj-in",
|
||||||
peer->host, pfx_buf);
|
peer->host, pfx_buf);
|
||||||
@ -4467,8 +4441,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
|||||||
/* Logging. */
|
/* Logging. */
|
||||||
if (bgp_debug_update(peer, p, NULL, 1)) {
|
if (bgp_debug_update(peer, p, NULL, 1)) {
|
||||||
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
|
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
|
||||||
addpath_id ? 1 : 0, addpath_id, pfx_buf,
|
addpath_id ? 1 : 0, addpath_id, NULL,
|
||||||
sizeof(pfx_buf));
|
pfx_buf, sizeof(pfx_buf));
|
||||||
zlog_debug("%s rcvd UPDATE about %s -- withdrawn", peer->host,
|
zlog_debug("%s rcvd UPDATE about %s -- withdrawn", peer->host,
|
||||||
pfx_buf);
|
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)) {
|
} else if (bgp_debug_update(peer, p, NULL, 1)) {
|
||||||
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
|
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
|
||||||
addpath_id ? 1 : 0, addpath_id, pfx_buf,
|
addpath_id ? 1 : 0, addpath_id, NULL,
|
||||||
sizeof(pfx_buf));
|
pfx_buf, sizeof(pfx_buf));
|
||||||
zlog_debug("%s Can't find the route %s", peer->host, 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();
|
json_nexthop_global = json_object_new_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (safi == SAFI_EVPN) {
|
||||||
|
if (!json_paths)
|
||||||
|
vty_out(vty, " Route %pRN", bn);
|
||||||
|
}
|
||||||
|
|
||||||
if (path->extra) {
|
if (path->extra) {
|
||||||
char tag_buf[30];
|
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 (safi == SAFI_EVPN) {
|
||||||
if (!json_paths) {
|
if (!json_paths) {
|
||||||
vty_out(vty, " Route %pFX",
|
|
||||||
(struct prefix_evpn *)
|
|
||||||
bgp_dest_get_prefix(bn));
|
|
||||||
if (tag_buf[0] != '\0')
|
if (tag_buf[0] != '\0')
|
||||||
vty_out(vty, " VNI %s", tag_buf);
|
vty_out(vty, " VNI %s", tag_buf);
|
||||||
vty_out(vty, "\n");
|
|
||||||
} else {
|
} else {
|
||||||
if (tag_buf[0])
|
if (tag_buf[0])
|
||||||
json_object_string_add(json_path, "VNI",
|
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 */
|
/* Line1 display AS-path, Aggregator */
|
||||||
if (attr->aspath) {
|
if (attr->aspath) {
|
||||||
if (json_paths) {
|
if (json_paths) {
|
||||||
|
@ -1084,6 +1084,71 @@ static const struct route_map_rule_cmd route_match_evpn_rd_cmd = {
|
|||||||
route_match_rd_free
|
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 */
|
/* Route map commands for VRF route leak with source vrf matching */
|
||||||
static enum route_map_cmd_result_t
|
static enum route_map_cmd_result_t
|
||||||
route_match_vrl_source_vrf(void *rule, const struct prefix *prefix,
|
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);
|
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,
|
DEFPY_YANG(match_vrl_source_vrf,
|
||||||
match_vrl_source_vrf_cmd,
|
match_vrl_source_vrf_cmd,
|
||||||
"match source-vrf NAME$vrf_name",
|
"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_evpn_default_route_cmd);
|
||||||
route_map_install_match(&route_match_vrl_source_vrf_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_table_id_cmd);
|
||||||
route_map_install_set(&route_set_srte_color_cmd);
|
route_map_install_set(&route_set_srte_color_cmd);
|
||||||
route_map_install_set(&route_set_ip_nexthop_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, &no_match_evpn_rd_cmd);
|
||||||
install_element(RMAP_NODE, &match_evpn_default_route_cmd);
|
install_element(RMAP_NODE, &match_evpn_default_route_cmd);
|
||||||
install_element(RMAP_NODE, &no_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, &match_vrl_source_vrf_cmd);
|
||||||
install_element(RMAP_NODE, &no_match_vrl_source_vrf_cmd);
|
install_element(RMAP_NODE, &no_match_vrl_source_vrf_cmd);
|
||||||
|
|
||||||
|
@ -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,
|
.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,
|
.xpath = NULL,
|
||||||
},
|
},
|
||||||
|
@ -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_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_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_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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -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);
|
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;
|
||||||
|
}
|
||||||
|
@ -862,6 +862,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
|
|||||||
bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p,
|
bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p,
|
||||||
label_pnt, num_labels,
|
label_pnt, num_labels,
|
||||||
addpath_encode, addpath_tx_id,
|
addpath_encode, addpath_tx_id,
|
||||||
|
&adv->baa->attr->evpn_overlay,
|
||||||
pfx_buf, sizeof(pfx_buf));
|
pfx_buf, sizeof(pfx_buf));
|
||||||
zlog_debug("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s",
|
zlog_debug("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s",
|
||||||
subgrp->update_group->id, subgrp->id,
|
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,
|
bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, NULL, 0,
|
||||||
addpath_encode, addpath_tx_id,
|
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",
|
zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE %s -- unreachable",
|
||||||
subgrp->update_group->id, subgrp->id,
|
subgrp->update_group->id, subgrp->id,
|
||||||
pfx_buf);
|
pfx_buf);
|
||||||
|
@ -1058,9 +1058,19 @@ static bool update_ipv4nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp,
|
|||||||
* connected routes leaked into a VRF.
|
* connected routes leaked into a VRF.
|
||||||
*/
|
*/
|
||||||
if (is_evpn) {
|
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 &&
|
} else if (nh_othervrf &&
|
||||||
api_nh->gate.ipv4.s_addr == INADDR_ANY) {
|
api_nh->gate.ipv4.s_addr == INADDR_ANY) {
|
||||||
api_nh->type = NEXTHOP_TYPE_IFINDEX;
|
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;
|
api_nh->vrf_id = nh_bgp->vrf_id;
|
||||||
|
|
||||||
if (is_evpn) {
|
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) {
|
} else if (nh_othervrf) {
|
||||||
if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) {
|
if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) {
|
||||||
api_nh->type = NEXTHOP_TYPE_IFINDEX;
|
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->label_num = 1;
|
||||||
api_nh->labels[0] = label;
|
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;
|
api_nh->weight = nh_weight;
|
||||||
|
|
||||||
if (mpinfo->extra
|
if (mpinfo->extra
|
||||||
@ -2805,6 +2830,7 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS)
|
|||||||
struct in_addr vtep_ip = {INADDR_ANY};
|
struct in_addr vtep_ip = {INADDR_ANY};
|
||||||
vrf_id_t tenant_vrf_id = VRF_DEFAULT;
|
vrf_id_t tenant_vrf_id = VRF_DEFAULT;
|
||||||
struct in_addr mcast_grp = {INADDR_ANY};
|
struct in_addr mcast_grp = {INADDR_ANY};
|
||||||
|
ifindex_t svi_ifindex = 0;
|
||||||
|
|
||||||
s = zclient->ibuf;
|
s = zclient->ibuf;
|
||||||
vni = stream_getl(s);
|
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);
|
vtep_ip.s_addr = stream_get_ipv4(s);
|
||||||
stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t));
|
stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t));
|
||||||
mcast_grp.s_addr = stream_get_ipv4(s);
|
mcast_grp.s_addr = stream_get_ipv4(s);
|
||||||
|
stream_get(&svi_ifindex, s, sizeof(ifindex_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
bgp = bgp_lookup_by_vrf_id(vrf_id);
|
bgp = bgp_lookup_by_vrf_id(vrf_id);
|
||||||
@ -2819,16 +2846,17 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (BGP_DEBUG(zebra, ZEBRA))
|
if (BGP_DEBUG(zebra, ZEBRA))
|
||||||
zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s",
|
zlog_debug(
|
||||||
(cmd == ZEBRA_VNI_ADD) ? "add" : "del",
|
"Rx VNI %s VRF %s VNI %u tenant-vrf %s SVI ifindex %u",
|
||||||
vrf_id_to_name(vrf_id), vni,
|
(cmd == ZEBRA_VNI_ADD) ? "add" : "del",
|
||||||
vrf_id_to_name(tenant_vrf_id));
|
vrf_id_to_name(vrf_id), vni,
|
||||||
|
vrf_id_to_name(tenant_vrf_id), svi_ifindex);
|
||||||
|
|
||||||
if (cmd == ZEBRA_VNI_ADD)
|
if (cmd == ZEBRA_VNI_ADD)
|
||||||
return bgp_evpn_local_vni_add(
|
return bgp_evpn_local_vni_add(
|
||||||
bgp, vni,
|
bgp, vni,
|
||||||
vtep_ip.s_addr != INADDR_ANY ? vtep_ip : bgp->router_id,
|
vtep_ip.s_addr != INADDR_ANY ? vtep_ip : bgp->router_id,
|
||||||
tenant_vrf_id, mcast_grp);
|
tenant_vrf_id, mcast_grp, svi_ifindex);
|
||||||
else
|
else
|
||||||
return bgp_evpn_local_vni_del(bgp, vni);
|
return bgp_evpn_local_vni_del(bgp, vni);
|
||||||
}
|
}
|
||||||
|
35
bgpd/bgpd.h
35
bgpd/bgpd.h
@ -510,16 +510,18 @@ struct bgp {
|
|||||||
uint16_t af_flags[AFI_MAX][SAFI_MAX];
|
uint16_t af_flags[AFI_MAX][SAFI_MAX];
|
||||||
#define BGP_CONFIG_DAMPENING (1 << 0)
|
#define BGP_CONFIG_DAMPENING (1 << 0)
|
||||||
/* l2vpn evpn flags - 1 << 0 is used for DAMPENNG */
|
/* l2vpn evpn flags - 1 << 0 is used for DAMPENNG */
|
||||||
#define BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST (1 << 1)
|
#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST (1 << 1)
|
||||||
#define BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST (1 << 2)
|
#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP (1 << 2)
|
||||||
#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 3)
|
#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST (1 << 3)
|
||||||
#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 4)
|
#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 */
|
/* import/export between address families */
|
||||||
#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 5)
|
#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 7)
|
||||||
#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 6)
|
#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 8)
|
||||||
/* vrf-route leaking flags */
|
/* vrf-route leaking flags */
|
||||||
#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 7)
|
#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 9)
|
||||||
#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 8)
|
#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 10)
|
||||||
|
|
||||||
/* BGP per AF peer count */
|
/* BGP per AF peer count */
|
||||||
uint32_t af_peer_count[AFI_MAX][SAFI_MAX];
|
uint32_t af_peer_count[AFI_MAX][SAFI_MAX];
|
||||||
@ -638,6 +640,14 @@ struct bgp {
|
|||||||
/* EVI hash table */
|
/* EVI hash table */
|
||||||
struct hash *vnihash;
|
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 */
|
/* EVPN enable - advertise gateway macip routes */
|
||||||
int advertise_gw_macip;
|
int advertise_gw_macip;
|
||||||
|
|
||||||
@ -683,6 +693,15 @@ struct bgp {
|
|||||||
/* Hash table of EVPN nexthops maintained per-tenant-VRF */
|
/* Hash table of EVPN nexthops maintained per-tenant-VRF */
|
||||||
struct hash *evpn_nh_table;
|
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 */
|
/* vrf flags */
|
||||||
uint32_t vrf_flags;
|
uint32_t vrf_flags;
|
||||||
#define BGP_VRF_AUTO (1 << 0)
|
#define BGP_VRF_AUTO (1 << 0)
|
||||||
|
109
doc/user/bgp.rst
109
doc/user/bgp.rst
@ -2696,6 +2696,115 @@ remote VTEP.
|
|||||||
Note that you should not enable both the advertise-svi-ip and the advertise-default-gw
|
Note that you should not enable both the advertise-svi-ip and the advertise-default-gw
|
||||||
at the same time.
|
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
|
EVPN Multihoming
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -366,6 +366,10 @@ DECLARE_QOBJ_TYPE(route_map);
|
|||||||
(strmatch(A, "frr-bgp-route-map:ipv4-vpn-address"))
|
(strmatch(A, "frr-bgp-route-map:ipv4-vpn-address"))
|
||||||
#define IS_SET_BGP_IPV4_NH(A) \
|
#define IS_SET_BGP_IPV4_NH(A) \
|
||||||
(strmatch(A, "frr-bgp-route-map:set-ipv4-nexthop"))
|
(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. */
|
/* Prototypes. */
|
||||||
extern void route_map_init(void);
|
extern void route_map_init(void);
|
||||||
|
@ -1248,6 +1248,16 @@ void route_map_action_show(struct vty *vty, struct lyd_node *dnode,
|
|||||||
yang_dnode_get_string(
|
yang_dnode_get_string(
|
||||||
dnode,
|
dnode,
|
||||||
"./rmap-set-action/frr-bgp-route-map:ipv4-nexthop"));
|
"./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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
] } }
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
] } }
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"vrfName": "vrf-blue",
|
||||||
|
"routerId": "10.100.0.1",
|
||||||
|
"defaultLocPrf": 100,
|
||||||
|
"localAS": 101,
|
||||||
|
"routes": { "100.0.0.21/32": null } }
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
] } }
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
] } }
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"vrfName": "vrf-blue",
|
||||||
|
"routerId": "10.100.0.1",
|
||||||
|
"defaultLocPrf": 100,
|
||||||
|
"localAS": 101,
|
||||||
|
"routes": { "100::21/128": null } }
|
30
tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf
Normal file
30
tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf
Normal 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
|
@ -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
|
||||||
|
!
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
] } }
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
] } }
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"vrfName": "vrf-blue",
|
||||||
|
"routerId": "10.100.0.2",
|
||||||
|
"defaultLocPrf": 100,
|
||||||
|
"localAS": 101,
|
||||||
|
"routes": { "100.0.0.21/32": null } }
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
] } }
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
] } }
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"vrfName": "vrf-blue",
|
||||||
|
"routerId": "10.100.0.2",
|
||||||
|
"defaultLocPrf": 100,
|
||||||
|
"localAS": 101,
|
||||||
|
"routes": { "100::21/128": null } }
|
14
tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf
Normal file
14
tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf
Normal 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
|
@ -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
|
||||||
|
!
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
!
|
||||||
|
int host1-eth0
|
||||||
|
ip address 50.0.1.11/24
|
||||||
|
ipv6 address 50:0:1::11/48
|
@ -0,0 +1 @@
|
|||||||
|
!
|
@ -0,0 +1,4 @@
|
|||||||
|
!
|
||||||
|
int host1-eth0
|
||||||
|
ip address 50.0.1.21/24
|
||||||
|
ipv6 address 50:0:1::21/48
|
@ -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))
|
@ -31,7 +31,29 @@ show bgp ipv6 statistics
|
|||||||
show bgp martian next-hop
|
show bgp martian next-hop
|
||||||
show bgp nexthop
|
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 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
|
CMD_LIST_END
|
||||||
|
|
||||||
# Zebra Support Bundle Command List
|
# Zebra Support Bundle Command List
|
||||||
|
@ -300,6 +300,18 @@ module frr-bgp-route-map {
|
|||||||
"Set BGP large community list (for deletion)";
|
"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 {
|
grouping extcommunity-non-transitive-types {
|
||||||
leaf two-octet-as-specific {
|
leaf two-octet-as-specific {
|
||||||
type boolean;
|
type boolean;
|
||||||
@ -816,5 +828,25 @@ module frr-bgp-route-map {
|
|||||||
type bgp-filter:bgp-list-name;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,10 @@ void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt)
|
|||||||
if (json == NULL) {
|
if (json == NULL) {
|
||||||
vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name);
|
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, " 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",
|
vty_out(vty, " Local VTEP IP: %pI4\n",
|
||||||
&zevpn->local_vtep_ip);
|
&zevpn->local_vtep_ip);
|
||||||
vty_out(vty, " Mcast group: %pI4\n",
|
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",
|
json_object_string_add(json, "vxlanInterface",
|
||||||
zevpn->vxlan_if->name);
|
zevpn->vxlan_if->name);
|
||||||
json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex);
|
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",
|
json_object_string_add(json, "vtepIp",
|
||||||
inet_ntop(AF_INET, &zevpn->local_vtep_ip,
|
inet_ntop(AF_INET, &zevpn->local_vtep_ip,
|
||||||
buf, sizeof(buf)));
|
buf, sizeof(buf)));
|
||||||
@ -1048,6 +1058,8 @@ int zebra_evpn_del(zebra_evpn_t *zevpn)
|
|||||||
zvrf = zebra_vrf_get_evpn();
|
zvrf = zebra_vrf_get_evpn();
|
||||||
assert(zvrf);
|
assert(zvrf);
|
||||||
|
|
||||||
|
zevpn->svi_if = NULL;
|
||||||
|
|
||||||
/* Free the neighbor hash table. */
|
/* Free the neighbor hash table. */
|
||||||
hash_free(zevpn->neigh_table);
|
hash_free(zevpn->neigh_table);
|
||||||
zevpn->neigh_table = NULL;
|
zevpn->neigh_table = NULL;
|
||||||
@ -1075,6 +1087,7 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn)
|
|||||||
{
|
{
|
||||||
struct zserv *client;
|
struct zserv *client;
|
||||||
struct stream *s;
|
struct stream *s;
|
||||||
|
ifindex_t svi_index;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
|
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)
|
if (!client)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
svi_index = zevpn->svi_if ? zevpn->svi_if->ifindex : 0;
|
||||||
|
|
||||||
s = stream_new(ZEBRA_MAX_PACKET_SIZ);
|
s = stream_new(ZEBRA_MAX_PACKET_SIZ);
|
||||||
|
|
||||||
zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id());
|
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_in_addr(s, &zevpn->local_vtep_ip);
|
||||||
stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */
|
stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */
|
||||||
stream_put_in_addr(s, &zevpn->mcast_grp);
|
stream_put_in_addr(s, &zevpn->mcast_grp);
|
||||||
|
stream_put(s, &svi_index, sizeof(ifindex_t));
|
||||||
|
|
||||||
/* Write packet size. */
|
/* Write packet size. */
|
||||||
stream_putw_at(s, 0, stream_get_endp(s));
|
stream_putw_at(s, 0, stream_get_endp(s));
|
||||||
|
|
||||||
if (IS_ZEBRA_DEBUG_VXLAN)
|
if (IS_ZEBRA_DEBUG_VXLAN)
|
||||||
zlog_debug("Send EVPN_ADD %u %pI4 tenant vrf %s to %s", zevpn->vni,
|
zlog_debug(
|
||||||
&zevpn->local_vtep_ip,
|
"Send EVPN_ADD %u %pI4 tenant vrf %s(%u) SVI index %u to %s",
|
||||||
vrf_id_to_name(zevpn->vrf_id),
|
zevpn->vni, &zevpn->local_vtep_ip,
|
||||||
zebra_route_string(client->proto));
|
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++;
|
client->vniadd_cnt++;
|
||||||
rc = zserv_send_message(client, s);
|
rc = zserv_send_message(client, s);
|
||||||
|
@ -98,6 +98,9 @@ struct zebra_evpn_t_ {
|
|||||||
/* Corresponding VxLAN interface. */
|
/* Corresponding VxLAN interface. */
|
||||||
struct interface *vxlan_if;
|
struct interface *vxlan_if;
|
||||||
|
|
||||||
|
/* Corresponding SVI interface. */
|
||||||
|
struct interface *svi_if;
|
||||||
|
|
||||||
/* List of remote VTEPs */
|
/* List of remote VTEPs */
|
||||||
zebra_vtep_t *vteps;
|
zebra_vtep_t *vteps;
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
#include "zebra/zebra_fpm_private.h"
|
#include "zebra/zebra_fpm_private.h"
|
||||||
#include "zebra/zebra_vxlan_private.h"
|
#include "zebra/zebra_vxlan_private.h"
|
||||||
|
#include "zebra/interface.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* af_addr_size
|
* af_addr_size
|
||||||
@ -164,7 +165,10 @@ static int netlink_route_info_add_nh(struct netlink_route_info *ri,
|
|||||||
{
|
{
|
||||||
struct netlink_nh_info nhi;
|
struct netlink_nh_info nhi;
|
||||||
union g_addr *src;
|
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));
|
memset(&nhi, 0, sizeof(nhi));
|
||||||
src = NULL;
|
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)) {
|
if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) {
|
||||||
nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN;
|
nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN;
|
||||||
|
|
||||||
zl3vni = zl3vni_from_vrf(nexthop->vrf_id);
|
/* Extract VNI id for the nexthop SVI interface */
|
||||||
if (zl3vni && is_l3vni_oper_up(zl3vni)) {
|
zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
|
||||||
|
if (zvrf) {
|
||||||
/* Add VNI to VxLAN encap info */
|
ifp = if_lookup_by_index_per_ns(zvrf->zns,
|
||||||
nhi.encap_info.vxlan_encap.vni = zl3vni->vni;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1012,6 +1012,7 @@ static int zevpn_build_hash_table_zns(struct ns *ns,
|
|||||||
vxl->access_vlan,
|
vxl->access_vlan,
|
||||||
zif->brslave_info.br_if);
|
zif->brslave_info.br_if);
|
||||||
if (vlan_if) {
|
if (vlan_if) {
|
||||||
|
zevpn->svi_if = vlan_if;
|
||||||
zevpn->vrf_id = vlan_if->vrf_id;
|
zevpn->vrf_id = vlan_if->vrf_id;
|
||||||
zl3vni = zl3vni_from_vrf(
|
zl3vni = zl3vni_from_vrf(
|
||||||
vlan_if->vrf_id);
|
vlan_if->vrf_id);
|
||||||
@ -1841,6 +1842,27 @@ static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp,
|
|||||||
return zl3vni;
|
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,
|
static inline void zl3vni_get_vrr_rmac(zebra_l3vni_t *zl3vni,
|
||||||
struct ethaddr *rmac)
|
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);
|
zevpn = zebra_evpn_from_svi(ifp, link_if);
|
||||||
|
|
||||||
if (zevpn) {
|
if (zevpn) {
|
||||||
|
zevpn->svi_if = NULL;
|
||||||
zevpn->vrf_id = VRF_DEFAULT;
|
zevpn->vrf_id = VRF_DEFAULT;
|
||||||
|
|
||||||
/* update the tenant vrf in BGP */
|
/* 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));
|
vrf_id_to_name(ifp->vrf_id));
|
||||||
|
|
||||||
/* update the vrf information for l2-vni and inform bgp */
|
/* update the vrf information for l2-vni and inform bgp */
|
||||||
|
zevpn->svi_if = ifp;
|
||||||
zevpn->vrf_id = ifp->vrf_id;
|
zevpn->vrf_id = ifp->vrf_id;
|
||||||
|
|
||||||
if (if_is_operative(zevpn->vxlan_if))
|
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,
|
vlan_if = zvni_map_to_svi(vxl->access_vlan,
|
||||||
zif->brslave_info.br_if);
|
zif->brslave_info.br_if);
|
||||||
if (vlan_if) {
|
if (vlan_if) {
|
||||||
|
zevpn->svi_if = vlan_if;
|
||||||
zevpn->vrf_id = vlan_if->vrf_id;
|
zevpn->vrf_id = vlan_if->vrf_id;
|
||||||
zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
|
zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
|
||||||
if (zl3vni)
|
if (zl3vni)
|
||||||
@ -4894,6 +4919,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
|
|||||||
struct zebra_l2info_vxlan *vxl = NULL;
|
struct zebra_l2info_vxlan *vxl = NULL;
|
||||||
zebra_evpn_t *zevpn = NULL;
|
zebra_evpn_t *zevpn = NULL;
|
||||||
zebra_l3vni_t *zl3vni = NULL;
|
zebra_l3vni_t *zl3vni = NULL;
|
||||||
|
struct interface *vlan_if = NULL;
|
||||||
|
|
||||||
/* Check if EVPN is enabled. */
|
/* Check if EVPN is enabled. */
|
||||||
if (!is_evpn_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)) {
|
&& (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
|
||||||
/* Delete from client, remove all remote VTEPs */
|
/* Delete from client, remove all remote VTEPs */
|
||||||
/* Also, free up all MACs and neighbors. */
|
/* Also, free up all MACs and neighbors. */
|
||||||
|
zevpn->svi_if = NULL;
|
||||||
zebra_evpn_send_del_to_client(zevpn);
|
zebra_evpn_send_del_to_client(zevpn);
|
||||||
zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH);
|
zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH);
|
||||||
zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC);
|
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);
|
zebra_evpn_es_set_base_evpn(zevpn);
|
||||||
}
|
}
|
||||||
zevpn_vxlan_if_set(zevpn, ifp, true /* set */);
|
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.
|
/* Take further actions needed.
|
||||||
* Note that if we are here, there is a change of interest.
|
* 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,
|
vlan_if = zvni_map_to_svi(vxl->access_vlan,
|
||||||
zif->brslave_info.br_if);
|
zif->brslave_info.br_if);
|
||||||
if (vlan_if) {
|
if (vlan_if) {
|
||||||
|
zevpn->svi_if = vlan_if;
|
||||||
zevpn->vrf_id = vlan_if->vrf_id;
|
zevpn->vrf_id = vlan_if->vrf_id;
|
||||||
zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
|
zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
|
||||||
if (zl3vni)
|
if (zl3vni)
|
||||||
|
@ -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_svi_if(zebra_l3vni_t *zl3vni);
|
||||||
extern struct interface *zl3vni_map_to_mac_vlan_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 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,
|
DECLARE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
|
||||||
bool delete, const char *reason), (rmac, zl3vni, delete, reason));
|
bool delete, const char *reason), (rmac, zl3vni, delete, reason));
|
||||||
|
Loading…
Reference in New Issue
Block a user