summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormitesh <mitesh@cumulusnetworks.com>2017-10-11 01:32:54 -0700
committerMitesh Kanjariya <mitesh@marvel-07.cumulusnetworks.com>2017-12-14 10:57:05 -0800
commitd3135ba31d46262e4bd9deaa65e62366cc65a033 (patch)
treec69315a89a17150d5fe6929e80a9dab9693bc582
parent10ebe1ab5417af8760a05fb2edd3306847f7e4ba (diff)
bgpd: program mac-ip routes in matching vrfs
Signed-off-by: Mitesh Kanjariya <mitesh@cumulusnetworks.com>
-rw-r--r--bgpd/bgp_evpn.c282
-rw-r--r--bgpd/bgp_evpn_private.h19
-rw-r--r--bgpd/bgp_evpn_vty.c74
-rw-r--r--bgpd/bgp_zebra.c14
-rw-r--r--lib/zebra.h1
-rw-r--r--zebra/zebra_rib.c3
-rw-r--r--zebra/zebra_vxlan.c12
-rw-r--r--zebra/zebra_vxlan.h1
-rw-r--r--zebra/zserv.c10
9 files changed, 411 insertions, 5 deletions
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index 326748bb47..4ddbbceb51 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -786,6 +786,95 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn,
* Calculate the best path for an EVPN route. Install/update best path in zebra,
* if appropriate.
*/
+static int evpn_vrf_route_select_install(struct bgp *bgp_vrf,
+ struct bgp_node *rn,
+ struct prefix *p,
+ afi_t afi,
+ safi_t safi)
+{
+ struct bgp_info *old_select, *new_select;
+ struct bgp_info_pair old_and_new;
+ int ret = 0;
+
+ /* Compute the best path. */
+ bgp_best_selection(bgp_vrf, rn, &bgp_vrf->maxpaths[afi][safi],
+ &old_and_new, afi, safi);
+ old_select = old_and_new.old;
+ new_select = old_and_new.new;
+
+ /* If the best path hasn't changed - see if there is still something to
+ * update
+ * to zebra RIB.
+ */
+ if (old_select && old_select == new_select
+ && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_NORMAL
+ && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)
+ && !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED)
+ && !bgp_vrf->addpath_tx_used[afi][safi]) {
+ if (bgp_zebra_has_route_changed(rn, old_select))
+ bgp_zebra_announce(rn, p, old_select, bgp_vrf,
+ afi, safi);
+ UNSET_FLAG(old_select->flags, BGP_INFO_MULTIPATH_CHG);
+ bgp_zebra_clear_route_change_flags(rn);
+ return ret;
+ }
+
+ /* If the user did a "clear" this flag will be set */
+ UNSET_FLAG(rn->flags, BGP_NODE_USER_CLEAR);
+
+ /* bestpath has changed; update relevant fields and install or uninstall
+ * into the zebra RIB.
+ */
+ if (old_select || new_select)
+ bgp_bump_version(rn);
+
+ if (old_select)
+ bgp_info_unset_flag(rn, old_select, BGP_INFO_SELECTED);
+ if (new_select) {
+ bgp_info_set_flag(rn, new_select, BGP_INFO_SELECTED);
+ bgp_info_unset_flag(rn, new_select, BGP_INFO_ATTR_CHANGED);
+ UNSET_FLAG(new_select->flags, BGP_INFO_MULTIPATH_CHG);
+ }
+
+ if (new_select && new_select->type == ZEBRA_ROUTE_BGP
+ && new_select->sub_type == BGP_ROUTE_NORMAL) {
+ bgp_zebra_announce(rn, p, new_select, bgp_vrf,
+ afi, safi);
+
+ /* If an old best existed and it was a "local" route, the only
+ * reason
+ * it would be supplanted is due to MAC mobility procedures. So,
+ * we
+ * need to do an implicit delete and withdraw that route from
+ * peers.
+ */
+ /*if (old_select && old_select->peer == bgp_vrf->peer_self
+ && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_STATIC) {
+ //evpn_delete_old_local_route(bgp, vpn, rn, old_select);
+ }*/ //TODO_MITESH: probably not needed for vrf routes, think!!
+ } else {
+ if (old_select && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_NORMAL) {
+ bgp_zebra_withdraw(p, old_select, safi);
+ }
+ }
+
+ /* Clear any route change flags. */
+ bgp_zebra_clear_route_change_flags(rn);
+
+ /* Reap old select bgp_info, if it has been removed */
+ if (old_select && CHECK_FLAG(old_select->flags, BGP_INFO_REMOVED))
+ bgp_info_reap(rn, old_select);
+
+ return ret;
+}
+
+/*
+ * Calculate the best path for an EVPN route. Install/update best path in zebra,
+ * if appropriate.
+ */
static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
struct bgp_node *rn)
{
@@ -1419,6 +1508,88 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
+ * Install route entry into the VRF routing table and invoke route selection.
+ */
+static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
+ struct prefix_evpn *evp,
+ struct bgp_info *parent_ri)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ struct attr *attr_new;
+ int ret;
+ struct prefix p;
+ struct prefix *pp = &p;
+ afi_t afi = 0;
+ safi_t safi = 0;
+
+ memset(pp, 0, sizeof(struct prefix));
+ ip_prefix_from_type2_prefix(evp, pp);
+
+ /* Create (or fetch) route within the VRF. */
+ /* NOTE: There is no RD here. */
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ afi = AFI_IP;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp);
+ } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) {
+ afi = AFI_IP6;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp);
+ } else
+ return 0;
+
+ /* Check if route entry is already present. */
+ for (ri = rn->info; ri; ri = ri->next)
+ if (ri->extra
+ && (struct bgp_info *)ri->extra->parent == parent_ri)
+ break;
+
+ if (!ri) {
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(parent_ri->attr);
+
+ /* Create new route with its attribute. */
+ ri = info_make(parent_ri->type, parent_ri->sub_type, 0,
+ parent_ri->peer, attr_new, rn);
+ SET_FLAG(ri->flags, BGP_INFO_VALID);
+ bgp_info_extra_get(ri);
+ ri->extra->parent = parent_ri;
+ if (parent_ri->extra)
+ memcpy(&ri->extra->label, &parent_ri->extra->label,
+ BGP_LABEL_BYTES);
+ bgp_info_add(rn, ri);
+ } else {
+ if (attrhash_cmp(ri->attr, parent_ri->attr)
+ && !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) {
+ bgp_unlock_node(rn);
+ return 0;
+ }
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(parent_ri->attr);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+ bgp_info_restore(rn, ri);
+
+ /* Mark if nexthop has changed. */
+ if (!IPV4_ADDR_SAME(&ri->attr->nexthop, &attr_new->nexthop))
+ SET_FLAG(ri->flags, BGP_INFO_IGP_CHANGED);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&ri->attr);
+ ri->attr = attr_new;
+ ri->uptime = bgp_clock();
+ }
+
+ /* Perform route selection and update zebra, if required. */
+ ret = evpn_vrf_route_select_install(bgp_vrf, rn, pp, afi, safi);
+
+ return ret;
+}
+
+/*
* Install route entry into the VNI routing table and invoke route selection.
*/
static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
@@ -1485,6 +1656,61 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
+ * Uninstall route entry from the VRF routing table and send message
+ * to zebra, if appropriate.
+ */
+static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
+ struct prefix_evpn *evp,
+ struct bgp_info *parent_ri)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ int ret;
+ struct prefix p;
+ struct prefix *pp = &p;
+ afi_t afi = 0;
+ safi_t safi = 0;
+
+ memset(pp, 0, sizeof(struct prefix));
+ ip_prefix_from_type2_prefix(evp, pp);
+
+ /* Locate route within the VRF. */
+ /* NOTE: There is no RD here. */
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ afi = AFI_IP;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp);
+ } else {
+ afi = AFI_IP6;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp);
+ }
+
+ if (!rn)
+ return 0;
+
+ /* Find matching route entry. */
+ for (ri = rn->info; ri; ri = ri->next)
+ if (ri->extra
+ && (struct bgp_info *)ri->extra->parent == parent_ri)
+ break;
+
+ if (!ri)
+ return 0;
+
+ /* Mark entry for deletion */
+ bgp_info_delete(rn, ri);
+
+ /* Perform route selection and update zebra, if required. */
+ ret = evpn_vrf_route_select_install(bgp_vrf, rn, pp, afi, safi);
+
+ /* Unlock route node. */
+ bgp_unlock_node(rn);
+
+ return ret;
+}
+
+/*
* Uninstall route entry from the VNI routing table and send message
* to zebra, if appropriate.
*/
@@ -1707,6 +1933,45 @@ static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
}
/*
+ * Install or uninstall route in matching VRFs (list).
+ */
+static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi,
+ safi_t safi, struct prefix_evpn *evp,
+ struct bgp_info *ri,
+ struct list *vrfs, int install)
+{
+ char buf[PREFIX2STR_BUFFER];
+ struct bgp *bgp_vrf;
+ struct listnode *node, *nnode;
+
+ /* Only type-2 routes go into a VRF */
+ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE))
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) {
+ int ret;
+
+ if (install)
+ ret = install_evpn_route_entry_in_vrf(bgp_vrf,
+ evp, ri);
+ else
+ ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf,
+ evp, ri);
+
+ if (ret) {
+ zlog_err("%u: Failed to %s prefix %s in VRF %s",
+ bgp_def->vrf_id,
+ install ? "install" : "uninstall",
+ prefix2str(evp, buf, sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*
* Install or uninstall route in matching VNIs (list).
*/
static int install_uninstall_route_in_vnis(struct bgp *bgp, afi_t afi,
@@ -1777,7 +2042,8 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
u_char type, sub_type;
struct ecommunity_val *eval;
struct ecommunity_val eval_tmp;
- struct irt_node *irt;
+ struct irt_node *irt; /* import rt for l2vni */
+ struct vrf_irt_node *vrf_irt; /* import rt for l3vni */
/* Only deal with RTs */
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
@@ -1788,12 +2054,18 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
if (sub_type != ECOMMUNITY_ROUTE_TARGET)
continue;
- /* Are we interested in this RT? */
+ /* Import route into matching l2-vnis */
irt = lookup_import_rt(bgp, eval);
if (irt && irt->vnis)
install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri,
irt->vnis, import);
+ /* Import route into matching l3-vnis (vrfs) */
+ vrf_irt = lookup_vrf_import_rt(eval);
+ if (vrf_irt && vrf_irt->vrfs)
+ install_uninstall_route_in_vrfs(bgp, afi, safi, evp, ri,
+ vrf_irt->vrfs, import);
+
/* Also check for non-exact match. In this, we mask out the AS
* and
* only check on the local-admin sub-field. This is to
@@ -1801,16 +2073,22 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
* VNI as the RT for EBGP peering too.
*/
irt = NULL;
+ vrf_irt = NULL;
if (type == ECOMMUNITY_ENCODE_AS
|| type == ECOMMUNITY_ENCODE_AS4
|| type == ECOMMUNITY_ENCODE_IP) {
memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
mask_ecom_global_admin(&eval_tmp, eval);
irt = lookup_import_rt(bgp, &eval_tmp);
+ vrf_irt = lookup_vrf_import_rt(&eval_tmp);
}
if (irt && irt->vnis)
install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri,
irt->vnis, import);
+ if (vrf_irt && vrf_irt->vrfs)
+ install_uninstall_route_in_vrfs(bgp, afi, safi, evp,
+ ri, vrf_irt->vrfs,
+ import);
}
return 0;
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
index f8594394ef..1b7e4d719e 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -258,6 +258,25 @@ static inline void encode_mac_mobility_extcomm(int static_mac, u_int32_t seq,
eval->val[7] = seq & 0xff;
}
+static inline void ip_prefix_from_type2_prefix(struct prefix_evpn *evp,
+ struct prefix *ip)
+{
+ memset(ip, 0, sizeof(struct prefix));
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ ip->family = AF_INET;
+ ip->prefixlen = IPV4_MAX_BITLEN;
+ memcpy(&(ip->u.prefix4),
+ &(evp->prefix.ip.ip),
+ IPV4_MAX_BYTELEN);
+ } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) {
+ ip->family = AF_INET6;
+ ip->prefixlen = IPV6_MAX_BITLEN;
+ memcpy(&(ip->u.prefix6),
+ &(evp->prefix.ip.ip),
+ IPV6_MAX_BYTELEN);
+ }
+}
+
static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
struct ethaddr *mac,
struct ipaddr *ip)
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 86b50db581..c3f77aba27 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -411,6 +411,52 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
json_object_object_add(json, "exportRts", json_export_rtl);
}
+static void evpn_show_vrf_routes(struct vty *vty,
+ struct bgp *bgp_vrf)
+{
+ struct bgp *bgp_def = NULL;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ int header = 1;
+ u_int32_t prefix_cnt, path_cnt;
+
+ prefix_cnt = path_cnt = 0;
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return;
+
+ for (rn = bgp_table_top(bgp_vrf->rib[AFI_L2VPN][SAFI_EVPN]); rn;
+ rn = bgp_route_next(rn)) {
+ char prefix_str[BUFSIZ];
+
+ bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str,
+ sizeof(prefix_str));
+
+ if (rn->info) {
+ /* Overall header/legend displayed once. */
+ if (header) {
+ bgp_evpn_show_route_header(vty, bgp_def, NULL);
+ header = 0;
+ }
+ prefix_cnt++;
+ }
+
+ /* For EVPN, the prefix is displayed for each path (to fit in
+ * with code that already exists).
+ */
+ for (ri = rn->info; ri; ri = ri->next) {
+ route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, NULL);
+ path_cnt++;
+ }
+ }
+
+ if (prefix_cnt == 0)
+ vty_out(vty, "No EVPN prefixes exist for this VRF");
+ else
+ vty_out(vty, "\nDisplayed %u prefixes (%u paths)",
+ prefix_cnt, path_cnt);
+}
+
static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
struct vty *vty, struct in_addr vtep_ip,
json_object *json)
@@ -2641,6 +2687,33 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip,
}
/*
+ * Display per-VRF EVPN routing table.
+ */
+DEFUN(show_bgp_l2vpn_evpn_route_vrf, show_bgp_l2vpn_evpn_route_vrf_cmd,
+ "show bgp l2vpn evpn route vrf VRFNAME",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "EVPN route information\n"
+ "VRF\n"
+ "VRF Name\n")
+{
+ int vrf_idx = 6;
+ char *vrf_name = NULL;
+ struct bgp *bgp_vrf = NULL;
+
+ vrf_name = argv[vrf_idx]->arg;
+ bgp_vrf = bgp_lookup_by_name(vrf_name);
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ evpn_show_vrf_routes(vty, bgp_vrf);
+
+ return CMD_SUCCESS;
+}
+
+/*
* Display per-VNI EVPN routing table.
*/
DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd,
@@ -3697,6 +3770,7 @@ void bgp_ethernetvpn_init(void)
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vrf_cmd);
install_element(VIEW_NODE,
&show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index a660139a24..78defa85dc 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -1015,6 +1015,12 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
if (info->sub_type == BGP_ROUTE_AGGREGATE)
zapi_route_set_blackhole(&api, BLACKHOLE_NULL);
+ /* If it is an EVPN route mark as such.
+ * Currently presence of rmac in attr denotes
+ * this is an EVPN type-2 route */
+ if (!is_zero_mac(&(info->attr->rmac)))
+ SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_TYPE2_ROUTE);
+
if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED
|| info->sub_type == BGP_ROUTE_AGGREGATE) {
SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
@@ -1072,7 +1078,13 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
api_nh = &api.nexthops[valid_nh_count];
api_nh->gate.ipv4 = *nexthop;
- api_nh->type = NEXTHOP_TYPE_IPV4;
+
+ /* EVPN type-2 routes are
+ programmed as onlink on l3-vni SVI */
+ if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_TYPE2_ROUTE))
+ api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ else
+ api_nh->type = NEXTHOP_TYPE_IPV4;
} else {
ifindex_t ifindex;
struct in6_addr *nexthop;
diff --git a/lib/zebra.h b/lib/zebra.h
index fa5fa89f77..73b5f7058e 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -409,6 +409,7 @@ extern const char *zserv_command_string(unsigned int command);
#define ZEBRA_FLAG_STATIC 0x40
#define ZEBRA_FLAG_SCOPE_LINK 0x100
#define ZEBRA_FLAG_FIB_OVERRIDE 0x200
+#define ZEBRA_FLAG_EVPN_TYPE2_ROUTE 0x400
/* ZEBRA_FLAG_BLACKHOLE was 0x04 */
/* ZEBRA_FLAG_REJECT was 0x80 */
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 58b6965995..82f087cebf 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -259,7 +259,8 @@ struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re,
/*Pending: need to think if null ifp here is ok during bootup?
There was a crash because ifp here was coming to be NULL */
if (ifp)
- if (connected_is_unnumbered(ifp)) {
+ if (connected_is_unnumbered(ifp) ||
+ CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_TYPE2_ROUTE)) {
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK);
}
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 962e66068d..03e9fd3185 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -6084,3 +6084,15 @@ void zebra_vxlan_ns_disable(struct zebra_ns *zns)
{
hash_free(zns->l3vni_table);
}
+
+/* get the l3vni svi ifindex */
+ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_vrf(vrf_id);
+ if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+ return 0;
+
+ return zl3vni->svi_if->ifindex;
+}
diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h
index 33e962c484..9e1ca746a5 100644
--- a/zebra/zebra_vxlan.h
+++ b/zebra/zebra_vxlan.h
@@ -51,6 +51,7 @@ is_evpn_enabled()
#define VNI_STR_LEN 32
+extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id);
extern int zebra_vxlan_vrf_delete(struct zebra_vrf *);
extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, u_char use_json);
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 4352b78e83..d82ccb93d7 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -1149,6 +1149,7 @@ static int zread_route_add(struct zserv *client, u_short length,
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) {
for (i = 0; i < api.nexthop_num; i++) {
api_nh = &api.nexthops[i];
+ ifindex_t ifindex = 0;
switch (api_nh->type) {
case NEXTHOP_TYPE_IFINDEX:
@@ -1160,9 +1161,16 @@ static int zread_route_add(struct zserv *client, u_short length,
re, &api_nh->gate.ipv4, NULL);
break;
case NEXTHOP_TYPE_IPV4_IFINDEX:
+ if (CHECK_FLAG(api.flags,
+ ZEBRA_FLAG_EVPN_TYPE2_ROUTE)) {
+ ifindex =
+ get_l3vni_svi_ifindex(zvrf_id(zvrf));
+ } else {
+ ifindex = api_nh->ifindex;
+ }
nexthop = route_entry_nexthop_ipv4_ifindex_add(
re, &api_nh->gate.ipv4, NULL,
- api_nh->ifindex);
+ ifindex);
break;
case NEXTHOP_TYPE_IPV6:
nexthop = route_entry_nexthop_ipv6_add(