]> git.proxmox.com Git - mirror_frr.git/blobdiff - bgpd/bgp_evpn.c
zebra: if multiple connecteds, select loopback or vrf if present
[mirror_frr.git] / bgpd / bgp_evpn.c
index 9f3fb3498c4f5ea2d28b65a60c4938a40eef281e..9e814516b73dfc31957b280020023ab0c717cac3 100644 (file)
@@ -43,6 +43,7 @@
 #include "bgpd/bgp_ecommunity.h"
 #include "bgpd/bgp_encap_types.h"
 #include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
 #include "bgpd/bgp_aspath.h"
 #include "bgpd/bgp_zebra.h"
 #include "bgpd/bgp_nexthop.h"
 extern struct zclient *zclient;
 
 DEFINE_QOBJ_TYPE(bgpevpn)
+DEFINE_QOBJ_TYPE(evpnes)
 
 
 /*
  * Static function declarations
  */
-static void delete_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
-                                   afi_t afi, safi_t safi, struct bgp_node *rn,
+static void delete_evpn_route_entry(struct bgp *bgp,
+                                   afi_t afi, safi_t safi,
+                                   struct bgp_node *rn,
                                    struct bgp_info **ri);
 static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn);
 
@@ -67,6 +70,47 @@ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn);
  * Private functions.
  */
 
+/* compare two IPV4 VTEP IPs */
+static int evpn_vtep_ip_cmp(const void *p1, const void *p2)
+{
+       const struct in_addr *ip1 = p1;
+       const struct in_addr *ip2 = p2;
+
+       if (!ip1 && !ip2)
+               return 1;
+       if (!ip1 || !ip2)
+               return 0;
+       return (ip1->s_addr == ip2->s_addr);
+}
+
+/*
+ * Make hash key for ESI.
+ */
+static unsigned int esi_hash_keymake(void *p)
+{
+       struct evpnes *pes = p;
+       const void *pnt = (void *)pes->esi.val;
+
+       return jhash(pnt, ESI_BYTES, 0xa5a5a55a);
+}
+
+/*
+ * Compare two ESIs.
+ */
+static int esi_cmp(const void *p1, const void *p2)
+{
+       const struct evpnes *pes1 = p1;
+       const struct evpnes *pes2 = p2;
+
+       if (pes1 == NULL && pes2 == NULL)
+               return 1;
+
+       if (pes1 == NULL || pes2 == NULL)
+               return 0;
+
+       return (memcmp(pes1->esi.val, pes2->esi.val, ESI_BYTES) == 0);
+}
+
 /*
  * Make vni hash key.
  */
@@ -129,7 +173,8 @@ static struct vrf_irt_node *vrf_import_rt_new(struct ecommunity_val *rt)
 
        bgp_def = bgp_get_default();
        if (!bgp_def) {
-               zlog_err("vrf import rt new - def instance not created yet");
+               flog_err(BGP_ERR_NO_DFLT,
+                         "vrf import rt new - def instance not created yet");
                return NULL;
        }
 
@@ -159,7 +204,8 @@ static void vrf_import_rt_free(struct vrf_irt_node *irt)
 
        bgp_def = bgp_get_default();
        if (!bgp_def) {
-               zlog_err("vrf import rt free - def instance not created yet");
+               flog_err(BGP_ERR_NO_DFLT,
+                         "vrf import rt free - def instance not created yet");
                return;
        }
 
@@ -180,7 +226,9 @@ static struct vrf_irt_node *lookup_vrf_import_rt(struct ecommunity_val *rt)
 
        bgp_def = bgp_get_default();
        if (!bgp_def) {
-               zlog_err("vrf import rt lookup - def instance not created yet");
+               flog_err(
+                       BGP_ERR_NO_DFLT,
+                       "vrf import rt lookup - def instance not created yet");
                return NULL;
        }
 
@@ -576,7 +624,8 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
        if (is_evpn_prefix_ipaddr_v4(p))
                stream_put_in_addr(s, &p->prefix.imet_addr.ip.ipaddr_v4);
        else if (is_evpn_prefix_ipaddr_v6(p)) {
-               zlog_err(
+               flog_err(
+                       BGP_ERR_VTEP_INVALID,
                        "Bad remote IP when trying to %s remote VTEP for VNI %u",
                        add ? "ADD" : "DEL", vpn->vni);
                return -1;
@@ -592,6 +641,40 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
        return zclient_send_message(zclient);
 }
 
+/*
+ * Build extended community for EVPN ES (type-4) route
+ */
+static void build_evpn_type4_route_extcomm(struct evpnes *es,
+                                          struct attr *attr)
+{
+       struct ecommunity ecom_encap;
+       struct ecommunity ecom_es_rt;
+       struct ecommunity_val eval;
+       struct ecommunity_val eval_es_rt;
+       bgp_encap_types tnl_type;
+       struct ethaddr mac;
+
+       /* Encap */
+       tnl_type = BGP_ENCAP_TYPE_VXLAN;
+       memset(&ecom_encap, 0, sizeof(ecom_encap));
+       encode_encap_extcomm(tnl_type, &eval);
+       ecom_encap.size = 1;
+       ecom_encap.val = (uint8_t *)eval.val;
+       attr->ecommunity = ecommunity_dup(&ecom_encap);
+
+       /* ES import RT */
+       memset(&mac, 0, sizeof(struct ethaddr));
+       memset(&ecom_es_rt, 0, sizeof(ecom_es_rt));
+       es_get_system_mac(&es->esi, &mac);
+       encode_es_rt_extcomm(&eval_es_rt, &mac);
+       ecom_es_rt.size = 1;
+       ecom_es_rt.val = (uint8_t *)eval_es_rt.val;
+       attr->ecommunity =
+               ecommunity_merge(attr->ecommunity, &ecom_es_rt);
+
+       attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
+}
+
 /*
  * Build extended communities for EVPN prefix route.
  */
@@ -619,11 +702,9 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,
 
        /* Add the export RTs for L3VNI/VRF */
        vrf_export_rtl = bgp_vrf->vrf_export_rtl;
-       if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
-               for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom))
-                       attr->ecommunity =
-                               ecommunity_merge(attr->ecommunity, ecom);
-       }
+       for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom))
+               attr->ecommunity =
+                       ecommunity_merge(attr->ecommunity, ecom);
 
        /* add the router mac extended community */
        if (!is_zero_mac(&attr->rmac)) {
@@ -655,10 +736,13 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
        struct ecommunity ecom_sticky;
        struct ecommunity ecom_default_gw;
        struct ecommunity ecom_rmac;
+       struct ecommunity ecom_na;
        struct ecommunity_val eval;
        struct ecommunity_val eval_sticky;
        struct ecommunity_val eval_default_gw;
        struct ecommunity_val eval_rmac;
+       struct ecommunity_val eval_na;
+
        bgp_encap_types tnl_type;
        struct listnode *node, *nnode;
        struct ecommunity *ecom;
@@ -723,6 +807,15 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
                        ecommunity_merge(attr->ecommunity, &ecom_default_gw);
        }
 
+       if (attr->router_flag) {
+               memset(&ecom_na, 0, sizeof(ecom_na));
+               encode_na_flag_extcomm(&eval_na, attr->router_flag);
+               ecom_na.size = 1;
+               ecom_na.val = (uint8_t *)eval_na.val;
+               attr->ecommunity = ecommunity_merge(attr->ecommunity,
+                                                  &ecom_na);
+       }
+
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
 }
 
@@ -833,7 +926,7 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn,
                                        (struct prefix *)&rn->p, &vpn->prd);
        if (global_rn) {
                /* Delete route entry in the global EVPN table. */
-               delete_evpn_route_entry(bgp, vpn, afi, safi, global_rn, &ri);
+               delete_evpn_route_entry(bgp, afi, safi, global_rn, &ri);
 
                /* Schedule for processing - withdraws to peers happen from
                 * this table.
@@ -847,6 +940,164 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn,
        bgp_info_delete(rn, old_local);
 }
 
+static struct in_addr *es_vtep_new(struct in_addr vtep)
+{
+       struct in_addr *ip;
+
+       ip = XCALLOC(MTYPE_BGP_EVPN_ES_VTEP, sizeof(struct in_addr));
+       if (!ip)
+               return NULL;
+
+       ip->s_addr = vtep.s_addr;
+       return ip;
+}
+
+static void es_vtep_free(struct in_addr *ip)
+{
+       XFREE(MTYPE_BGP_EVPN_ES_VTEP, ip);
+}
+
+/* check if VTEP is already part of the list */
+static int is_vtep_present_in_list(struct list *list,
+                                  struct in_addr vtep)
+{
+       struct listnode *node = NULL;
+       struct in_addr *tmp;
+
+       for (ALL_LIST_ELEMENTS_RO(list, node, tmp)) {
+               if (tmp->s_addr == vtep.s_addr)
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * Best path for ES route was changed,
+ * update the list of VTEPs for this ES
+ */
+static int evpn_es_install_vtep(struct bgp *bgp,
+                               struct evpnes *es,
+                               struct prefix_evpn *p,
+                               struct in_addr rvtep)
+{
+       struct in_addr *vtep_ip;
+
+       if (is_vtep_present_in_list(es->vtep_list, rvtep))
+               return 0;
+
+
+       vtep_ip = es_vtep_new(rvtep);
+       if (vtep_ip)
+               listnode_add_sort(es->vtep_list, vtep_ip);
+       return 0;
+}
+
+/*
+ * Best path for ES route was changed,
+ * update the list of VTEPs for this ES
+ */
+static int evpn_es_uninstall_vtep(struct bgp *bgp,
+                                 struct evpnes *es,
+                                 struct prefix_evpn *p,
+                                 struct in_addr rvtep)
+{
+       struct listnode *node, *nnode, *node_to_del = NULL;
+       struct in_addr *tmp;
+
+       for (ALL_LIST_ELEMENTS(es->vtep_list, node, nnode, tmp)) {
+               if (tmp->s_addr == rvtep.s_addr) {
+                       es_vtep_free(tmp);
+                       node_to_del = node;
+               }
+       }
+
+       if (node_to_del)
+               list_delete_node(es->vtep_list, node_to_del);
+
+       return 0;
+}
+
+/*
+ * Calculate the best path for a ES(type-4) route.
+ */
+static int evpn_es_route_select_install(struct bgp *bgp,
+                                       struct evpnes *es,
+                                       struct bgp_node *rn)
+{
+       int ret = 0;
+       afi_t afi = AFI_L2VPN;
+       safi_t safi = SAFI_EVPN;
+       struct bgp_info *old_select; /* old best */
+       struct bgp_info *new_select; /* new best */
+       struct bgp_info_pair old_and_new;
+
+       /* Compute the best path. */
+       bgp_best_selection(bgp, rn, &bgp->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 something needs to be
+        * updated
+        */
+       if (old_select && old_select == new_select
+           && old_select->type == ZEBRA_ROUTE_BGP
+           && old_select->sub_type == BGP_ROUTE_IMPORTED
+           && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)
+           && !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED)
+           && !bgp->addpath_tx_used[afi][safi]) {
+               if (bgp_zebra_has_route_changed(rn, old_select)) {
+                       ret = evpn_es_install_vtep(bgp, es,
+                                                  (struct prefix_evpn *)&rn->p,
+                                                  old_select->attr->nexthop);
+               }
+               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_IMPORTED) {
+               ret = evpn_es_install_vtep(bgp, es,
+                                          (struct prefix_evpn *)&rn->p,
+                                          new_select->attr->nexthop);
+       } else {
+               if (old_select && old_select->type == ZEBRA_ROUTE_BGP
+                   && old_select->sub_type == BGP_ROUTE_IMPORTED)
+                       ret = evpn_es_uninstall_vtep(
+                               bgp, es, (struct prefix_evpn *)&rn->p,
+                               old_select->attr->nexthop);
+       }
+
+       /* 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.
@@ -856,6 +1107,7 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
 {
        struct bgp_info *old_select, *new_select;
        struct bgp_info_pair old_and_new;
+       struct prefix_evpn *evp;
        afi_t afi = AFI_L2VPN;
        safi_t safi = SAFI_EVPN;
        int ret = 0;
@@ -867,6 +1119,7 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
        old_select = old_and_new.old;
        new_select = old_and_new.new;
 
+       evp = (struct prefix_evpn *)&rn->p;
        /* If the best path hasn't changed - see if there is still something to
         * update
         * to zebra RIB.
@@ -882,6 +1135,10 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
                                SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
                        if (old_select->attr->default_gw)
                                SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+                       if (is_evpn_prefix_ipaddr_v6(evp) &&
+                           old_select->attr->router_flag)
+                               SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+
                        ret = evpn_zebra_install(
                                bgp, vpn, (struct prefix_evpn *)&rn->p,
                                old_select->attr->nexthop, flags);
@@ -915,6 +1172,10 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
                        SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
                if (new_select->attr->default_gw)
                        SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+               if (is_evpn_prefix_ipaddr_v6(evp) &&
+                   new_select->attr->router_flag)
+                       SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+
                ret = evpn_zebra_install(bgp, vpn, (struct prefix_evpn *)&rn->p,
                                         new_select->attr->nexthop, flags);
                /* If an old best existed and it was a "local" route, the only
@@ -991,6 +1252,180 @@ static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_node *rn)
        return local_ri->attr->sticky;
 }
 
+/*
+ * create or update EVPN type4 route entry.
+ * This could be in the ES table or the global table.
+ * TODO: handle remote ES (type4) routes as well
+ */
+static int update_evpn_type4_route_entry(struct bgp *bgp,
+                                        struct evpnes *es,
+                                        afi_t afi, safi_t safi,
+                                        struct bgp_node *rn,
+                                        struct attr *attr,
+                                        int add,
+                                        struct bgp_info **ri,
+                                        int *route_changed)
+{
+       char buf[ESI_STR_LEN];
+       char buf1[INET6_ADDRSTRLEN];
+       struct bgp_info *tmp_ri = NULL;
+       struct bgp_info *local_ri = NULL; /* local route entry if any */
+       struct bgp_info *remote_ri = NULL; /* remote route entry if any */
+       struct attr *attr_new = NULL;
+       struct prefix_evpn *evp = NULL;
+
+       *ri = NULL;
+       *route_changed = 1;
+       evp = (struct prefix_evpn *)&rn->p;
+
+       /* locate the local and remote entries if any */
+       for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) {
+               if (tmp_ri->peer == bgp->peer_self &&
+                   tmp_ri->type == ZEBRA_ROUTE_BGP &&
+                   tmp_ri->sub_type == BGP_ROUTE_STATIC)
+                       local_ri = tmp_ri;
+               if (tmp_ri->type == ZEBRA_ROUTE_BGP &&
+                   tmp_ri->sub_type == BGP_ROUTE_IMPORTED &&
+                   CHECK_FLAG(tmp_ri->flags, BGP_INFO_VALID))
+                       remote_ri = tmp_ri;
+       }
+
+       /* we don't expect to see a remote_ri at this point.
+        * An ES route has esi + vtep_ip as the key,
+        * We shouldn't see the same route from any other vtep.
+        */
+       if (remote_ri) {
+               flog_err(
+                       BGP_ERR_ES_INVALID,
+                       "%u ERROR: local es route for ESI: %s Vtep %s also learnt from remote",
+                       bgp->vrf_id,
+                       esi_to_str(&evp->prefix.es_addr.esi, buf, sizeof(buf)),
+                       ipaddr2str(&es->originator_ip, buf1, sizeof(buf1)));
+               return -1;
+       }
+
+       if (!local_ri && !add)
+               return 0;
+
+       /* create or update the entry */
+       if (!local_ri) {
+
+               /* Add or update attribute to hash */
+               attr_new = bgp_attr_intern(attr);
+
+               /* Create new route with its attribute. */
+               tmp_ri = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC,
+                                  0, bgp->peer_self, attr_new, rn);
+               SET_FLAG(tmp_ri->flags, BGP_INFO_VALID);
+
+               /* add the newly created path to the route-node */
+               bgp_info_add(rn, tmp_ri);
+       } else {
+               tmp_ri = local_ri;
+               if (attrhash_cmp(tmp_ri->attr, attr)
+                   && !CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
+                       *route_changed = 0;
+               else {
+                       /* The attribute has changed.
+                        * Add (or update) attribute to hash. */
+                       attr_new = bgp_attr_intern(attr);
+                       bgp_info_set_flag(rn, tmp_ri, BGP_INFO_ATTR_CHANGED);
+
+                       /* Restore route, if needed. */
+                       if (CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
+                               bgp_info_restore(rn, tmp_ri);
+
+                       /* Unintern existing, set to new. */
+                       bgp_attr_unintern(&tmp_ri->attr);
+                       tmp_ri->attr = attr_new;
+                       tmp_ri->uptime = bgp_clock();
+               }
+       }
+
+       /* Return back the route entry. */
+       *ri = tmp_ri;
+       return 0;
+}
+
+/* update evpn es (type-4) route */
+static int update_evpn_type4_route(struct bgp *bgp,
+                                  struct evpnes *es,
+                                  struct prefix_evpn *p)
+{
+       int ret = 0;
+       int route_changed = 0;
+       char buf[ESI_STR_LEN];
+       char buf1[INET6_ADDRSTRLEN];
+       afi_t afi = AFI_L2VPN;
+       safi_t safi = SAFI_EVPN;
+       struct attr attr;
+       struct attr *attr_new = NULL;
+       struct bgp_node *rn = NULL;
+       struct bgp_info *ri = NULL;
+
+       memset(&attr, 0, sizeof(struct attr));
+
+       /* Build path-attribute for this route. */
+       bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
+       attr.nexthop = es->originator_ip.ipaddr_v4;
+       attr.mp_nexthop_global_in = es->originator_ip.ipaddr_v4;
+       attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+
+       /* Set up extended community. */
+       build_evpn_type4_route_extcomm(es, &attr);
+
+       /* First, create (or fetch) route node within the ESI. */
+       /* NOTE: There is no RD here. */
+       rn = bgp_node_get(es->route_table, (struct prefix *)p);
+
+       /* Create or update route entry. */
+       ret = update_evpn_type4_route_entry(bgp, es, afi, safi, rn,
+                                           &attr, 1, &ri,
+                                           &route_changed);
+       if (ret != 0) {
+               flog_err(
+                       BGP_ERR_ES_INVALID,
+                       "%u ERROR: Failed to updated ES route ESI: %s VTEP %s",
+                       bgp->vrf_id,
+                       esi_to_str(&p->prefix.es_addr.esi, buf, sizeof(buf)),
+                       ipaddr2str(&es->originator_ip, buf1, sizeof(buf1)));
+       }
+
+       assert(ri);
+       attr_new = ri->attr;
+
+       /* Perform route selection;
+        * this is just to set the flags correctly
+        * as local route in the ES always wins.
+        */
+       evpn_es_route_select_install(bgp, es, rn);
+       bgp_unlock_node(rn);
+
+       /* If this is a new route or some attribute has changed, export the
+        * route to the global table. The route will be advertised to peers
+        * from there. Note that this table is a 2-level tree (RD-level +
+        * Prefix-level) similar to L3VPN routes.
+        */
+       if (route_changed) {
+               struct bgp_info *global_ri;
+
+               rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi,
+                                     (struct prefix *)p, &es->prd);
+               update_evpn_type4_route_entry(bgp, es, afi, safi,
+                                             rn, attr_new,
+                                             1, &global_ri,
+                                             &route_changed);
+
+               /* Schedule for processing and unlock node. */
+               bgp_process(bgp, rn, afi, safi);
+               bgp_unlock_node(rn);
+       }
+
+       /* Unintern temporary. */
+       aspath_unintern(&attr.aspath);
+       return 0;
+}
+
 static int update_evpn_type5_route_entry(struct bgp *bgp_def,
                                         struct bgp *bgp_vrf, afi_t afi,
                                         safi_t safi, struct bgp_node *rn,
@@ -1011,8 +1446,9 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_def,
                        local_ri = tmp_ri;
        }
 
-       /* create a new route entry if one doesnt exist.
-          Otherwise see if route attr has changed
+       /*
+        * create a new route entry if one doesnt exist.
+        * Otherwise see if route attr has changed
         */
        if (!local_ri) {
 
@@ -1171,8 +1607,7 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
        if (!local_ri) {
                /* When learnt locally for the first time but already known from
                 * remote, we have to initiate appropriate MAC mobility steps.
-                * This
-                * is applicable when updating the VNI routing table.
+                * This is applicable when updating the VNI routing table.
                 * We need to skip mobility steps for g/w macs (local mac on g/w
                 * SVI) advertised in EVPN.
                 * This will ensure that local routes are preferred for g/w macs
@@ -1291,6 +1726,8 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
        attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
        attr.sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? 1 : 0;
        attr.default_gw = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? 1 : 0;
+       attr.router_flag = CHECK_FLAG(flags,
+                                     ZEBRA_MACIP_TYPE_ROUTER_FLAG) ? 1 : 0;
 
        /* PMSI is only needed for type-3 routes */
        if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE)
@@ -1308,7 +1745,8 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
        if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
            (is_evpn_prefix_ipaddr_v4(p) ||
             !IN6_IS_ADDR_LINKLOCAL(&p->prefix.macip_addr.ip.ipaddr_v6)) &&
-           CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS))
+           CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) &&
+           bgpevpn_get_l3vni(vpn))
                add_l3_ecomm = 1;
 
        /* Set up extended community. */
@@ -1354,19 +1792,22 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
        return 0;
 }
 
-/* Delete EVPN type5 route entry from global table */
-static void delete_evpn_type5_route_entry(struct bgp *bgp_def,
-                                         struct bgp *bgp_vrf, afi_t afi,
-                                         safi_t safi, struct bgp_node *rn,
-                                         struct bgp_info **ri)
+/*
+ * Delete EVPN route entry.
+ * The entry can be in ESI/VNI table or the global table.
+ */
+static void delete_evpn_route_entry(struct bgp *bgp,
+                                   afi_t afi, safi_t safi,
+                                   struct bgp_node *rn,
+                                   struct bgp_info **ri)
 {
-       struct bgp_info *tmp_ri = NULL;
+       struct bgp_info *tmp_ri;
 
        *ri = NULL;
 
-       /* find the matching route entry */
+       /* Now, find matching route. */
        for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next)
-               if (tmp_ri->peer == bgp_def->peer_self
+               if (tmp_ri->peer == bgp->peer_self
                    && tmp_ri->type == ZEBRA_ROUTE_BGP
                    && tmp_ri->sub_type == BGP_ROUTE_STATIC)
                        break;
@@ -1378,6 +1819,57 @@ static void delete_evpn_type5_route_entry(struct bgp *bgp_def,
                bgp_info_delete(rn, tmp_ri);
 }
 
+
+
+/* Delete EVPN ES (type-4) route */
+static int delete_evpn_type4_route(struct bgp *bgp,
+                                  struct evpnes *es,
+                                  struct prefix_evpn *p)
+{
+       afi_t afi = AFI_L2VPN;
+       safi_t safi = SAFI_EVPN;
+       struct bgp_info *ri;
+       struct bgp_node *rn = NULL; /* rn in esi table */
+       struct bgp_node *global_rn = NULL; /* rn in global table */
+
+       /* First, locate the route node within the ESI.
+        * If it doesn't exist, ther is nothing to do.
+        * Note: there is no RD here.
+        */
+       rn = bgp_node_lookup(es->route_table, (struct prefix *)p);
+       if (!rn)
+               return 0;
+
+       /* Next, locate route node in the global EVPN routing table.
+        * Note that this table is a 2-level tree (RD-level + Prefix-level)
+        */
+       global_rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi,
+                                       (struct prefix *)p, &es->prd);
+       if (global_rn) {
+
+               /* Delete route entry in the global EVPN table. */
+               delete_evpn_route_entry(bgp, afi, safi,
+                                       global_rn, &ri);
+
+               /* Schedule for processing - withdraws to peers happen from
+                * this table.
+                */
+               if (ri)
+                       bgp_process(bgp, global_rn, afi, safi);
+               bgp_unlock_node(global_rn);
+       }
+
+       /*
+        * Delete route entry in the ESI route table.
+        * This can just be removed.
+        */
+       delete_evpn_route_entry(bgp, afi, safi, rn, &ri);
+       if (ri)
+               bgp_info_reap(rn, ri);
+       bgp_unlock_node(rn);
+       return 0;
+}
+
 /* Delete EVPN type5 route */
 static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp)
 {
@@ -1397,39 +1889,13 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp)
        if (!rn)
                return 0;
 
-       delete_evpn_type5_route_entry(bgp_def, bgp_vrf, afi, safi, rn, &ri);
+       delete_evpn_route_entry(bgp_def, afi, safi, rn, &ri);
        if (ri)
                bgp_process(bgp_def, rn, afi, safi);
        bgp_unlock_node(rn);
        return 0;
 }
 
-/*
- * Delete EVPN route entry. This could be in the VNI route table
- * or the global route table.
- */
-static void delete_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
-                                   afi_t afi, safi_t safi, struct bgp_node *rn,
-                                   struct bgp_info **ri)
-{
-       struct bgp_info *tmp_ri;
-
-       *ri = NULL;
-
-       /* Now, find matching route. */
-       for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next)
-               if (tmp_ri->peer == bgp->peer_self
-                   && tmp_ri->type == ZEBRA_ROUTE_BGP
-                   && tmp_ri->sub_type == BGP_ROUTE_STATIC)
-                       break;
-
-       *ri = tmp_ri;
-
-       /* Mark route for delete. */
-       if (tmp_ri)
-               bgp_info_delete(rn, tmp_ri);
-}
-
 /*
  * Delete EVPN route (of type based on prefix) for specified VNI and
  * schedule for processing.
@@ -1459,7 +1925,7 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
                                        (struct prefix *)p, &vpn->prd);
        if (global_rn) {
                /* Delete route entry in the global EVPN table. */
-               delete_evpn_route_entry(bgp, vpn, afi, safi, global_rn, &ri);
+               delete_evpn_route_entry(bgp, afi, safi, global_rn, &ri);
 
                /* Schedule for processing - withdraws to peers happen from
                 * this table.
@@ -1471,7 +1937,7 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
 
        /* Delete route entry in the VNI route table. This can just be removed.
         */
-       delete_evpn_route_entry(bgp, vpn, afi, safi, rn, &ri);
+       delete_evpn_route_entry(bgp, afi, safi, rn, &ri);
        if (ri)
                bgp_info_reap(rn, ri);
        bgp_unlock_node(rn);
@@ -1532,7 +1998,8 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
        /* Add L3 VNI RTs and RMAC for non IPv6 link-local attributes if
         * using L3 VNI for type-2 routes also.
         */
-       if (CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS))
+       if (CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) &&
+           bgpevpn_get_l3vni(vpn))
                add_l3_ecomm = 1;
 
        build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm);
@@ -1561,11 +2028,13 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
                                update_evpn_route_entry(bgp, vpn, afi, safi, rn,
                                                        &attr_sticky, 0, 1, &ri,
                                                        0);
-                       else if (evpn_route_is_def_gw(bgp, rn))
+                       else if (evpn_route_is_def_gw(bgp, rn)) {
+                               if (is_evpn_prefix_ipaddr_v6(evp))
+                                       attr_def_gw.router_flag = 1;
                                update_evpn_route_entry(bgp, vpn, afi, safi, rn,
                                                        &attr_def_gw, 0, 1, &ri,
                                                        0);
-                       else
+                       else
                                update_evpn_route_entry(bgp, vpn, afi, safi, rn,
                                                        &attr, 0, 1, &ri, 0);
                }
@@ -1629,7 +2098,7 @@ static int delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
                        if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
                                continue;
 
-                       delete_evpn_route_entry(bgp, vpn, afi, safi, rn, &ri);
+                       delete_evpn_route_entry(bgp, afi, safi, rn, &ri);
                        if (ri)
                                bgp_process(bgp, rn, afi, safi);
                }
@@ -1670,7 +2139,7 @@ static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
                if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
                        continue;
 
-               delete_evpn_route_entry(bgp, vpn, afi, safi, rn, &ri);
+               delete_evpn_route_entry(bgp, afi, safi, rn, &ri);
 
                /* Route entry in local table gets deleted immediately. */
                if (ri)
@@ -1680,6 +2149,27 @@ static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
        return 0;
 }
 
+/*
+ * Delete all routes in per ES route-table
+ */
+static int delete_all_es_routes(struct bgp *bgp, struct evpnes *es)
+{
+       struct bgp_node *rn;
+       struct bgp_info *ri, *nextri;
+
+       /* Walk this ES's route table and delete all routes. */
+       for (rn = bgp_table_top(es->route_table); rn;
+            rn = bgp_route_next(rn)) {
+               for (ri = rn->info; (ri != NULL) && (nextri = ri->next, 1);
+                    ri = nextri) {
+                       bgp_info_delete(rn, ri);
+                       bgp_info_reap(rn, ri);
+               }
+       }
+
+       return 0;
+}
+
 /*
  * Delete all routes in the per-VNI route table.
  */
@@ -1723,6 +2213,29 @@ static int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
        return update_all_type2_routes(bgp, vpn);
 }
 
+/* Delete (and withdraw) local routes for specified ES from global and ES table.
+ * Also remove all other routes from the per ES table.
+ * Invoked when ES is deleted.
+ */
+static int delete_routes_for_es(struct bgp *bgp, struct evpnes *es)
+{
+       int ret;
+       char buf[ESI_STR_LEN];
+       struct prefix_evpn p;
+
+       /* Delete and withdraw locally learnt ES route */
+       build_evpn_type4_prefix(&p, &es->esi, es->originator_ip.ipaddr_v4);
+       ret = delete_evpn_type4_route(bgp, es, &p);
+       if (ret) {
+               flog_err(BGP_ERR_EVPN_ROUTE_DELETE,
+                         "%u failed to delete type-4 route for ESI %s",
+                         bgp->vrf_id, esi_to_str(&es->esi, buf, sizeof(buf)));
+       }
+
+       /* Delete all routes from per ES table */
+       return delete_all_es_routes(bgp, es);
+}
+
 /*
  * Delete (and withdraw) local routes for specified VNI from the global
  * table and per-VNI table. After this, remove all other routes from
@@ -1785,6 +2298,68 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn,
        return 0;
 }
 
+/* Install EVPN route entry in ES */
+static int install_evpn_route_entry_in_es(struct bgp *bgp,
+                                         struct evpnes *es,
+                                         struct prefix_evpn *p,
+                                         struct bgp_info *parent_ri)
+{
+       int ret = 0;
+       struct bgp_node *rn = NULL;
+       struct bgp_info *ri = NULL;
+       struct attr *attr_new = NULL;
+
+       /* Create (or fetch) route within the VNI.
+        * NOTE: There is no RD here.
+        */
+       rn = bgp_node_get(es->route_table, (struct prefix *)p);
+
+       /* 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, BGP_ROUTE_IMPORTED, 0,
+                              parent_ri->peer, attr_new, rn);
+               SET_FLAG(ri->flags, BGP_INFO_VALID);
+               bgp_info_extra_get(ri);
+               ri->extra->parent = parent_ri;
+               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_es_route_select_install(bgp, es, rn);
+       return ret;
+}
+
 /*
  * Install route entry into the VRF routing table and invoke route selection.
  */
@@ -1966,6 +2541,47 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
        return ret;
 }
 
+/* Uninstall EVPN route entry from ES route table */
+static int uninstall_evpn_route_entry_in_es(struct bgp *bgp,
+                                           struct evpnes *es,
+                                           struct prefix_evpn *p,
+                                           struct bgp_info *parent_ri)
+{
+       int ret;
+       struct bgp_node *rn;
+       struct bgp_info *ri;
+
+       if (!es->route_table)
+               return 0;
+
+       /* Locate route within the ESI.
+        * NOTE: There is no RD here.
+        */
+       rn = bgp_node_lookup(es->route_table, (struct prefix *)p);
+       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_es_route_select_install(bgp, es, rn);
+
+       /* Unlock route node. */
+       bgp_unlock_node(rn);
+
+       return ret;
+}
+
 /*
  * Uninstall route entry from the VRF routing table and send message
  * to zebra, if appropriate.
@@ -2070,6 +2686,22 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
        return ret;
 }
 
+/*
+ * Given a prefix, see if it belongs to ES.
+ */
+static int is_prefix_matching_for_es(struct prefix_evpn *p,
+                                    struct evpnes *es)
+{
+       /* if not an ES route return false */
+       if (p->prefix.route_type != BGP_EVPN_ES_ROUTE)
+               return 0;
+
+       if (memcmp(&p->prefix.es_addr.esi, &es->esi, sizeof(esi_t)) == 0)
+               return 1;
+
+       return 0;
+}
+
 /*
  * Given a route entry and a VRF, see if this route entry should be
  * imported into the VRF i.e., RTs match.
@@ -2175,31 +2807,99 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn,
                if (sub_type != ECOMMUNITY_ROUTE_TARGET)
                        continue;
 
-               /* See if this RT matches specified VNIs import RTs */
-               irt = lookup_import_rt(bgp, eval);
-               if (irt)
-                       if (is_vni_present_in_irt_vnis(irt->vnis, vpn))
-                               return 1;
+               /* See if this RT matches specified VNIs import RTs */
+               irt = lookup_import_rt(bgp, eval);
+               if (irt)
+                       if (is_vni_present_in_irt_vnis(irt->vnis, vpn))
+                               return 1;
+
+               /* 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
+                * facilitate using
+                * VNI as the RT for EBGP peering too.
+                */
+               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);
+               }
+               if (irt)
+                       if (is_vni_present_in_irt_vnis(irt->vnis, vpn))
+                               return 1;
+       }
+
+       return 0;
+}
+
+static int install_uninstall_routes_for_es(struct bgp *bgp,
+                                          struct evpnes *es,
+                                          int install)
+{
+       int ret;
+       afi_t afi;
+       safi_t safi;
+       char buf[PREFIX_STRLEN];
+       char buf1[ESI_STR_LEN];
+       struct bgp_node *rd_rn, *rn;
+       struct bgp_table *table;
+       struct bgp_info *ri;
+
+       afi = AFI_L2VPN;
+       safi = SAFI_EVPN;
+
+       /*
+        * Walk entire global routing table and evaluate routes which could be
+        * imported into this VRF. Note that we need to loop through all global
+        * routes to determine which route matches the import rt on vrf
+        */
+       for (rd_rn = bgp_table_top(bgp->rib[afi][safi]); rd_rn;
+            rd_rn = bgp_route_next(rd_rn)) {
+               table = (struct bgp_table *)(rd_rn->info);
+               if (!table)
+                       continue;
+
+               for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
+                       struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+                       for (ri = rn->info; ri; ri = ri->next) {
+                               /*
+                                * Consider "valid" remote routes applicable for
+                                * this ES.
+                                */
+                               if (!(CHECK_FLAG(ri->flags, BGP_INFO_VALID)
+                                     && ri->type == ZEBRA_ROUTE_BGP
+                                     && ri->sub_type == BGP_ROUTE_NORMAL))
+                                       continue;
 
-               /* 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
-                * facilitate using
-                * VNI as the RT for EBGP peering too.
-                */
-               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);
+                               if (!is_prefix_matching_for_es(evp, es))
+                                       continue;
+
+                               if (install)
+                                       ret = install_evpn_route_entry_in_es(
+                                               bgp, es, evp, ri);
+                               else
+                                       ret = uninstall_evpn_route_entry_in_es(
+                                               bgp, es, evp, ri);
+
+                               if (ret) {
+                                       flog_err(
+                                               BGP_ERR_EVPN_FAIL,
+                                               "Failed to %s EVPN %s route in ESI %s",
+                                               install ? "install"
+                                                       : "uninstall",
+                                               prefix2str(evp, buf,
+                                                          sizeof(buf)),
+                                               esi_to_str(&es->esi, buf1,
+                                                          sizeof(buf1)));
+                                       return ret;
+                               }
+                       }
                }
-               if (irt)
-                       if (is_vni_present_in_irt_vnis(irt->vnis, vpn))
-                               return 1;
        }
-
        return 0;
 }
 
@@ -2266,7 +2966,8 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install)
                                                        bgp_vrf, evp, ri);
 
                                        if (ret) {
-                                               zlog_err(
+                                               flog_err(
+                                                       BGP_ERR_EVPN_FAIL,
                                                        "Failed to %s EVPN %s route in VRF %s",
                                                        install ? "install"
                                                                : "uninstall",
@@ -2339,7 +3040,8 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp,
                                                        bgp, vpn, evp, ri);
 
                                        if (ret) {
-                                               zlog_err(
+                                               flog_err(
+                                                       BGP_ERR_EVPN_FAIL,
                                                        "%u: Failed to %s EVPN %s route in VNI %u",
                                                        bgp->vrf_id,
                                                        install ? "install"
@@ -2358,6 +3060,15 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp,
        return 0;
 }
 
+/* Install any existing remote ES routes applicable for this ES into its routing
+ * table. This is invoked when ES comes up.
+ */
+static int install_routes_for_es(struct bgp *bgp, struct evpnes *es)
+{
+       return install_uninstall_routes_for_es(bgp, es, 1);
+}
+
+
 /* Install any existing remote routes applicable for this VRF into VRF RIB. This
  * is invoked upon l3vni-add or l3vni import rt change
  */
@@ -2416,6 +3127,32 @@ static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
                                                0);
 }
 
+/* Install or unistall route in ES */
+static int install_uninstall_route_in_es(struct bgp *bgp, struct evpnes *es,
+                                        afi_t afi, safi_t safi,
+                                        struct prefix_evpn *evp,
+                                        struct bgp_info *ri,
+                                        int install)
+{
+       int ret = 0;
+       char buf[ESI_STR_LEN];
+
+       if (install)
+               ret = install_evpn_route_entry_in_es(bgp, es, evp, ri);
+       else
+               ret = uninstall_evpn_route_entry_in_es(bgp, es, evp, ri);
+
+       if (ret) {
+               flog_err(
+                       BGP_ERR_EVPN_FAIL,
+                       "%u: Failed to %s EVPN %s route in ESI %s", bgp->vrf_id,
+                       install ? "install" : "uninstall", "ES",
+                       esi_to_str(&evp->prefix.es_addr.esi, buf, sizeof(buf)));
+               return ret;
+       }
+       return 0;
+}
+
 /*
  * Install or uninstall route in matching VRFs (list).
  */
@@ -2449,11 +3186,12 @@ static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi,
                                                                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));
+                       flog_err(BGP_ERR_EVPN_FAIL,
+                                 "%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;
                }
        }
@@ -2484,12 +3222,14 @@ static int install_uninstall_route_in_vnis(struct bgp *bgp, afi_t afi,
                        ret = uninstall_evpn_route_entry(bgp, vpn, evp, ri);
 
                if (ret) {
-                       zlog_err("%u: Failed to %s EVPN %s route in VNI %u",
-                                bgp->vrf_id, install ? "install" : "uninstall",
-                                evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
-                                        ? "MACIP"
-                                        : "IMET",
-                                vpn->vni);
+                       flog_err(
+                               BGP_ERR_EVPN_FAIL,
+                               "%u: Failed to %s EVPN %s route in VNI %u",
+                               bgp->vrf_id, install ? "install" : "uninstall",
+                               evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+                                       ? "MACIP"
+                                       : "IMET",
+                               vpn->vni);
                        return ret;
                }
        }
@@ -2498,7 +3238,7 @@ static int install_uninstall_route_in_vnis(struct bgp *bgp, afi_t afi,
 }
 
 /*
- * Install or uninstall route for appropriate VNIs.
+ * Install or uninstall route for appropriate VNIs/ESIs.
  */
 static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
                                        struct prefix *p, struct bgp_info *ri,
@@ -2511,9 +3251,10 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
 
        assert(attr);
 
-       /* Only type-2 and type-3 and type-5 are supported currently */
+       /* Only type-2, type-3, type-4 and type-5 are supported currently */
        if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
              || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE
+             || evp->prefix.route_type == BGP_EVPN_ES_ROUTE
              || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
                return 0;
 
@@ -2525,9 +3266,8 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
        if (!ecom || !ecom->size)
                return -1;
 
-       /* For each extended community RT, see which VNIs/VRFs match and import
-        * the route into matching VNIs/VRFs.
-        */
+       /* An EVPN route belongs to a VNI or a VRF or an ESI based on the RTs
+        * attached to the route */
        for (i = 0; i < ecom->size; i++) {
                uint8_t *pnt;
                uint8_t type, sub_type;
@@ -2535,6 +3275,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
                struct ecommunity_val eval_tmp;
                struct irt_node *irt;    /* import rt for l2vni */
                struct vrf_irt_node *vrf_irt; /* import rt for l3vni */
+               struct evpnes *es;
 
                /* Only deal with RTs */
                pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
@@ -2545,51 +3286,79 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
                if (sub_type != ECOMMUNITY_ROUTE_TARGET)
                        continue;
 
-               /* Import route into matching l2-vnis (type-2/type-3 routes go
-                * into l2vni table)
+               /*
+                * macip routes (type-2) are imported into VNI and VRF tables.
+                * IMET route is imported into VNI table.
+                * prefix routes are imported into VRF table.
                 */
-               irt = lookup_import_rt(bgp, eval);
-               if (irt)
-                       install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri,
-                                                       irt->vnis, import);
+               if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE ||
+                   evp->prefix.route_type == BGP_EVPN_IMET_ROUTE ||
+                   evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
 
-               /* Import route into matching l3-vnis (type-2/type-5 routes go
-                * into l3vni/vrf table)
-                */
-               vrf_irt = lookup_vrf_import_rt(eval);
-               if (vrf_irt)
-                       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 facilitate using
-                * 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);
+                       irt = lookup_import_rt(bgp, eval);
+                       if (irt)
+                               install_uninstall_route_in_vnis(bgp, afi, safi,
+                                                               evp, ri,
+                                                               irt->vnis,
+                                                               import);
+
+                       vrf_irt = lookup_vrf_import_rt(eval);
+                       if (vrf_irt)
+                               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 facilitate using
+                        * 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)
+                               install_uninstall_route_in_vnis(bgp, afi, safi,
+                                                               evp, ri,
+                                                               irt->vnis,
+                                                               import);
+                       if (vrf_irt)
+                               install_uninstall_route_in_vrfs(bgp, afi, safi,
+                                                               evp, ri,
+                                                               vrf_irt->vrfs,
+                                                               import);
+               }
+
+               /* es route is imported into the es table */
+               if (evp->prefix.route_type == BGP_EVPN_ES_ROUTE) {
+
+                       /* we will match based on the entire esi to avoid
+                        * imoort of an es route for esi2 into esi1
+                        */
+                       es = bgp_evpn_lookup_es(bgp, &evp->prefix.es_addr.esi);
+                       if (es && is_es_local(es))
+                               install_uninstall_route_in_es(bgp, es,
+                                                             afi, safi,
+                                                             evp, ri, import);
                }
-               if (irt)
-                       install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri,
-                                                       irt->vnis, import);
-               if (vrf_irt)
-                       install_uninstall_route_in_vrfs(bgp, afi, safi, evp, ri,
-                                                       vrf_irt->vrfs, import);
        }
 
        return 0;
 }
 
-/* delete and withdraw all ipv4 and ipv6 routes in the vrf table as type-5
- * routes */
+/*
+ * delete and withdraw all ipv4 and ipv6 routes in the vrf table as type-5
+ * routes
+ */
 static void delete_withdraw_vrf_routes(struct bgp *bgp_vrf)
 {
        /* delete all ipv4 routes and withdraw from peers */
@@ -2601,8 +3370,10 @@ static void delete_withdraw_vrf_routes(struct bgp *bgp_vrf)
                bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST);
 }
 
-/* update and advertise all ipv4 and ipv6 routes in thr vrf table as type-5
- * routes */
+/*
+ * update and advertise all ipv4 and ipv6 routes in thr vrf table as type-5
+ * routes
+ */
 static void update_advertise_vrf_routes(struct bgp *bgp_vrf)
 {
        /* update all ipv4 routes */
@@ -2750,7 +3521,7 @@ static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
                                        (struct prefix *)&p, &vpn->prd);
        if (global_rn) {
                /* Delete route entry in the global EVPN table. */
-               delete_evpn_route_entry(bgp, vpn, afi, safi, global_rn, &ri);
+               delete_evpn_route_entry(bgp, afi, safi, global_rn, &ri);
 
                /* Schedule for processing - withdraws to peers happen from
                 * this table.
@@ -2771,14 +3542,7 @@ static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
  */
 static void update_router_id_vni(struct hash_backet *backet, struct bgp *bgp)
 {
-       struct bgpevpn *vpn;
-
-       vpn = (struct bgpevpn *)backet->data;
-
-       if (!vpn) {
-               zlog_warn("%s: VNI hash entry for VNI not found", __FUNCTION__);
-               return;
-       }
+       struct bgpevpn *vpn = (struct bgpevpn *)backet->data;
 
        /* Skip VNIs with configured RD. */
        if (is_rd_configured(vpn))
@@ -2796,14 +3560,7 @@ static void update_router_id_vni(struct hash_backet *backet, struct bgp *bgp)
  */
 static void withdraw_router_id_vni(struct hash_backet *backet, struct bgp *bgp)
 {
-       struct bgpevpn *vpn;
-
-       vpn = (struct bgpevpn *)backet->data;
-
-       if (!vpn) {
-               zlog_warn("%s: VNI hash entry for VNI not found", __FUNCTION__);
-               return;
-       }
+       struct bgpevpn *vpn = (struct bgpevpn *)backet->data;
 
        /* Skip VNIs with configured RD. */
        if (is_rd_configured(vpn))
@@ -2837,8 +3594,9 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
         */
        if (psize != 33 && psize != 37 && psize != 49 && psize != 36
            && psize != 40 && psize != 52) {
-               zlog_err("%u:%s - Rx EVPN Type-2 NLRI with invalid length %d",
-                        peer->bgp->vrf_id, peer->host, psize);
+               flog_err(BGP_ERR_EVPN_ROUTE_INVALID,
+                         "%u:%s - Rx EVPN Type-2 NLRI with invalid length %d",
+                         peer->bgp->vrf_id, peer->host, psize);
                return -1;
        }
 
@@ -2853,7 +3611,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
        /* Make EVPN prefix. */
        memset(&p, 0, sizeof(struct prefix_evpn));
        p.family = AF_EVPN;
-       p.prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN;
+       p.prefixlen = EVPN_ROUTE_PREFIXLEN;
        p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
 
        /* Copy Ethernet Seg Identifier */
@@ -2873,7 +3631,8 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
                memcpy(&p.prefix.macip_addr.mac.octet, pfx, ETH_ALEN);
                pfx += ETH_ALEN;
        } else {
-               zlog_err(
+               flog_err(
+                       BGP_ERR_EVPN_ROUTE_INVALID,
                        "%u:%s - Rx EVPN Type-2 NLRI with unsupported MAC address length %d",
                        peer->bgp->vrf_id, peer->host, macaddr_len);
                return -1;
@@ -2884,7 +3643,8 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
        ipaddr_len = *pfx++;
        if (ipaddr_len != 0 && ipaddr_len != IPV4_MAX_BITLEN
            && ipaddr_len != IPV6_MAX_BITLEN) {
-               zlog_err(
+               flog_err(
+                       BGP_ERR_EVPN_ROUTE_INVALID,
                        "%u:%s - Rx EVPN Type-2 NLRI with unsupported IP address length %d",
                        peer->bgp->vrf_id, peer->host, ipaddr_len);
                return -1;
@@ -2945,8 +3705,9 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi,
         * IP len (1) and IP (4 or 16).
         */
        if (psize != 17 && psize != 29) {
-               zlog_err("%u:%s - Rx EVPN Type-3 NLRI with invalid length %d",
-                        peer->bgp->vrf_id, peer->host, psize);
+               flog_err(BGP_ERR_EVPN_ROUTE_INVALID,
+                         "%u:%s - Rx EVPN Type-3 NLRI with invalid length %d",
+                         peer->bgp->vrf_id, peer->host, psize);
                return -1;
        }
 
@@ -2972,7 +3733,7 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi,
        /* Make EVPN prefix. */
        memset(&p, 0, sizeof(struct prefix_evpn));
        p.family = AF_EVPN;
-       p.prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN;
+       p.prefixlen = EVPN_ROUTE_PREFIXLEN;
        p.prefix.route_type = BGP_EVPN_IMET_ROUTE;
 
        /* Copy Ethernet Tag */
@@ -2986,7 +3747,8 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi,
                p.prefix.imet_addr.ip.ipa_type = IPADDR_V4;
                memcpy(&p.prefix.imet_addr.ip.ip.addr, pfx, IPV4_MAX_BYTELEN);
        } else {
-               zlog_err(
+               flog_err(
+                       BGP_ERR_EVPN_ROUTE_INVALID,
                        "%u:%s - Rx EVPN Type-3 NLRI with unsupported IP address length %d",
                        peer->bgp->vrf_id, peer->host, ipaddr_len);
                return -1;
@@ -3004,6 +3766,68 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi,
        return ret;
 }
 
+/*
+ * Process received EVPN type-4 route (advertise or withdraw).
+ */
+static int process_type4_route(struct peer *peer, afi_t afi, safi_t safi,
+                              struct attr *attr, uint8_t *pfx, int psize,
+                              uint32_t addpath_id)
+{
+       int ret;
+       esi_t esi;
+       uint8_t ipaddr_len;
+       struct in_addr vtep_ip;
+       struct prefix_rd prd;
+       struct prefix_evpn p;
+
+       /* Type-4 route should be either 23 or 35 bytes
+        *  RD (8), ESI (10), ip-len (1), ip (4 or 16)
+        */
+       if (psize != 23 && psize != 35) {
+               flog_err(BGP_ERR_EVPN_ROUTE_INVALID,
+                         "%u:%s - Rx EVPN Type-4 NLRI with invalid length %d",
+                         peer->bgp->vrf_id, peer->host, psize);
+               return -1;
+       }
+
+       /* Make prefix_rd */
+       prd.family = AF_UNSPEC;
+       prd.prefixlen = 64;
+       memcpy(&prd.val, pfx, 8);
+       pfx += 8;
+
+       /* get the ESI */
+       memcpy(&esi, pfx, ESI_BYTES);
+       pfx += ESI_BYTES;
+
+
+       /* Get the IP. */
+       ipaddr_len = *pfx++;
+       if (ipaddr_len == IPV4_MAX_BITLEN) {
+               memcpy(&vtep_ip, pfx, IPV4_MAX_BYTELEN);
+       } else {
+               flog_err(
+                       BGP_ERR_EVPN_ROUTE_INVALID,
+                       "%u:%s - Rx EVPN Type-4 NLRI with unsupported IP address length %d",
+                       peer->bgp->vrf_id, peer->host, ipaddr_len);
+               return -1;
+       }
+
+       build_evpn_type4_prefix(&p, &esi, vtep_ip);
+       /* Process the route. */
+       if (attr) {
+               ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
+                                afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+                                &prd, NULL, 0, 0, NULL);
+       } else {
+               ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
+                                  afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+                                  &prd, NULL, 0, NULL);
+       }
+       return ret;
+}
+
+
 /*
  * Process received EVPN type-5 route (advertise or withdraw).
  */
@@ -3025,8 +3849,9 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
         * Note that the IP and GW should both be IPv4 or both IPv6.
         */
        if (psize != 34 && psize != 58) {
-               zlog_err("%u:%s - Rx EVPN Type-5 NLRI with invalid length %d",
-                        peer->bgp->vrf_id, peer->host, psize);
+               flog_err(BGP_ERR_EVPN_ROUTE_INVALID,
+                         "%u:%s - Rx EVPN Type-5 NLRI with invalid length %d",
+                         peer->bgp->vrf_id, peer->host, psize);
                return -1;
        }
 
@@ -3039,7 +3864,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
        /* Make EVPN prefix. */
        memset(&p, 0, sizeof(struct prefix_evpn));
        p.family = AF_EVPN;
-       p.prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN;
+       p.prefixlen = EVPN_ROUTE_PREFIXLEN;
        p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
 
        /* Additional information outside of prefix - ESI and GW IP */
@@ -3057,7 +3882,8 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
        /* Fetch IP prefix length. */
        ippfx_len = *pfx++;
        if (ippfx_len > IPV6_MAX_BITLEN) {
-               zlog_err(
+               flog_err(
+                       BGP_ERR_EVPN_ROUTE_INVALID,
                        "%u:%s - Rx EVPN Type-5 NLRI with invalid IP Prefix length %d",
                        peer->bgp->vrf_id, peer->host, ippfx_len);
                return -1;
@@ -3178,9 +4004,8 @@ static void cleanup_vni_on_disable(struct hash_backet *backet, struct bgp *bgp)
  */
 static void free_vni_entry(struct hash_backet *backet, struct bgp *bgp)
 {
-       struct bgpevpn *vpn;
+       struct bgpevpn *vpn = (struct bgpevpn *)backet->data;
 
-       vpn = (struct bgpevpn *)backet->data;
        delete_all_vni_routes(bgp, vpn);
        bgp_evpn_free(bgp, vpn);
 }
@@ -3252,11 +4077,6 @@ static void update_autort_vni(struct hash_backet *backet, struct bgp *bgp)
 {
        struct bgpevpn *vpn = backet->data;
 
-       if (!vpn) {
-               zlog_warn("%s: VNI hash entry for VNI not found", __PRETTY_FUNCTION__);
-               return;
-       }
-
        if (!is_import_rt_configured(vpn)) {
                if (is_vni_live(vpn))
                        bgp_evpn_uninstall_routes(bgp, vpn);
@@ -3289,7 +4109,8 @@ void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, struct prefix *p,
        build_type5_prefix_from_ip_prefix(&evp, p);
        ret = delete_evpn_type5_route(bgp_vrf, &evp);
        if (ret) {
-               zlog_err(
+               flog_err(
+                       BGP_ERR_EVPN_ROUTE_DELETE,
                        "%u failed to delete type-5 route for prefix %s in vrf %s",
                        bgp_vrf->vrf_id, prefix2str(p, buf, sizeof(buf)),
                        vrf_id_to_name(bgp_vrf->vrf_id));
@@ -3335,8 +4156,9 @@ void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct prefix *p,
        build_type5_prefix_from_ip_prefix(&evp, p);
        ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr);
        if (ret)
-               zlog_err("%u: Failed to create type-5 route for prefix %s",
-                        bgp_vrf->vrf_id, prefix2str(p, buf, sizeof(buf)));
+               flog_err(BGP_ERR_EVPN_ROUTE_CREATE,
+                         "%u: Failed to create type-5 route for prefix %s",
+                         bgp_vrf->vrf_id, prefix2str(p, buf, sizeof(buf)));
 }
 
 /* Inject all prefixes of a particular address-family (currently, IPv4 or
@@ -3453,8 +4275,9 @@ void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,
        if (node_to_del)
                list_delete_node(bgp_vrf->vrf_import_rtl, node_to_del);
 
+       assert(bgp_vrf->vrf_import_rtl);
        /* fallback to auto import rt, if this was the last RT */
-       if (list_isempty(bgp_vrf->vrf_import_rtl)) {
+       if (bgp_vrf->vrf_import_rtl && list_isempty(bgp_vrf->vrf_import_rtl)) {
                UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
                evpn_auto_rt_import_add_for_vrf(bgp_vrf);
        }
@@ -3497,8 +4320,18 @@ void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
        if (node_to_del)
                list_delete_node(bgp_vrf->vrf_export_rtl, node_to_del);
 
+       /*
+        * Temporary assert to make SA happy.
+        * The ALL_LIST_ELEMENTS macro above has a NULL check
+        * which means that SA is going to complain about
+        * the list_isempty call, which doesn't NULL check.
+        * So until we get this situation cleaned up, here
+        * we are.
+        */
+       assert(bgp_vrf->vrf_export_rtl);
+
        /* fall back to auto-generated RT if this was the last RT */
-       if (bgp_vrf->vrf_export_rtl && list_isempty(bgp_vrf->vrf_export_rtl)) {
+       if (list_isempty(bgp_vrf->vrf_export_rtl)) {
                UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
                evpn_auto_rt_export_add_for_vrf(bgp_vrf);
        }
@@ -3695,6 +4528,7 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len)
 {
        char buf1[ETHER_ADDR_STRLEN];
        char buf2[PREFIX2STR_BUFFER];
+       char buf3[ESI_STR_LEN];
 
        if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) {
                snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type,
@@ -3736,6 +4570,13 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len)
                         is_evpn_prefix_ipaddr_v4(p)
                                 ? inet_ntoa(p->prefix.prefix_addr.ip.ipaddr_v4)
                                 : inet6_ntoa(p->prefix.prefix_addr.ip.ipaddr_v6));
+       } else if (p->prefix.route_type == BGP_EVPN_ES_ROUTE) {
+               snprintf(buf, len, "[%d]:[%s]:[%d]:[%s]",
+                        p->prefix.route_type,
+                        esi_to_str(&p->prefix.es_addr.esi, buf3, sizeof(buf3)),
+                        is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BITLEN
+                                                    : IPV6_MAX_BITLEN,
+                        inet_ntoa(p->prefix.es_addr.ip.ipaddr_v4));
        } else {
                /* For EVPN route types not supported yet. */
                snprintf(buf, len, "(unsupported route type %d)",
@@ -3801,6 +4642,15 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p,
                stream_put_in_addr(s, &evp->prefix.imet_addr.ip.ipaddr_v4);
                break;
 
+       case BGP_EVPN_ES_ROUTE:
+               stream_putc(s, 23); /* TODO: length: assumes ipv4 VTEP */
+               stream_put(s, prd->val, 8); /* RD */
+               stream_put(s, evp->prefix.es_addr.esi.val, 10); /* ESI */
+               stream_putc(s, IPV4_MAX_BITLEN); /* IP address Length - bits */
+               /* VTEP IP */
+               stream_put_in_addr(s, &evp->prefix.es_addr.ip.ipaddr_v4);
+               break;
+
        case BGP_EVPN_IP_PREFIX_ROUTE:
                /* TODO: AddPath support. */
                evpn_mpattr_encode_type5(s, p, prd, label, num_labels, attr);
@@ -3822,7 +4672,6 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
        int addpath_encoded;
        int psize = 0;
        uint8_t rtype;
-       uint8_t rlen;
        struct prefix p;
 
        /* Start processing the NLRI - there may be multiple in the MP_REACH */
@@ -3856,7 +4705,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
                        return -1;
 
                rtype = *pnt++;
-               psize = rlen = *pnt++;
+               psize = *pnt++;
 
                /* When packet overflow occur return immediately. */
                if (pnt + psize > lim)
@@ -3867,7 +4716,8 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
                        if (process_type2_route(peer, afi, safi,
                                                withdraw ? NULL : attr, pnt,
                                                psize, addpath_id)) {
-                               zlog_err(
+                               flog_err(
+                                       BGP_ERR_EVPN_FAIL,
                                        "%u:%s - Error in processing EVPN type-2 NLRI size %d",
                                        peer->bgp->vrf_id, peer->host, psize);
                                return -1;
@@ -3878,17 +4728,31 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
                        if (process_type3_route(peer, afi, safi,
                                                withdraw ? NULL : attr, pnt,
                                                psize, addpath_id)) {
-                               zlog_err(
+                               flog_err(
+                                       BGP_ERR_PKT_PROCESS,
                                        "%u:%s - Error in processing EVPN type-3 NLRI size %d",
                                        peer->bgp->vrf_id, peer->host, psize);
                                return -1;
                        }
                        break;
 
+               case BGP_EVPN_ES_ROUTE:
+                       if (process_type4_route(peer, afi, safi,
+                                               withdraw ? NULL : attr, pnt,
+                                               psize, addpath_id)) {
+                               flog_err(
+                                       BGP_ERR_PKT_PROCESS,
+                                       "%u:%s - Error in processing EVPN type-4 NLRI size %d",
+                                       peer->bgp->vrf_id, peer->host, psize);
+                               return -1;
+                       }
+                       break;
+
                case BGP_EVPN_IP_PREFIX_ROUTE:
                        if (process_type5_route(peer, afi, safi, attr, pnt,
                                                psize, addpath_id, withdraw)) {
-                               zlog_err(
+                               flog_err(
+                                       BGP_ERR_PKT_PROCESS,
                                        "%u:%s - Error in processing EVPN type-5 NLRI size %d",
                                        peer->bgp->vrf_id, peer->host, psize);
                                return -1;
@@ -4067,6 +4931,23 @@ void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn)
        UNSET_FLAG(vpn->flags, VNI_FLAG_RD_CFGD);
 }
 
+/*
+ * Lookup L3-VNI
+ */
+bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni)
+{
+       struct list *inst = bm->bgp;
+       struct listnode *node;
+       struct bgp *bgp_vrf;
+
+       for (ALL_LIST_ELEMENTS_RO(inst, node, bgp_vrf)) {
+               if (bgp_vrf->l3vni == vni)
+                       return true;
+       }
+
+       return false;
+}
+
 /*
  * Lookup VNI.
  */
@@ -4148,7 +5029,81 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
 }
 
 /*
- * Import route into matching VNI(s).
+ * Lookup local ES.
+ */
+struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi)
+{
+       struct evpnes *es;
+       struct evpnes tmp;
+
+       memset(&tmp, 0, sizeof(struct evpnes));
+       memcpy(&tmp.esi, esi, sizeof(esi_t));
+       es = hash_lookup(bgp->esihash, &tmp);
+       return es;
+}
+
+/*
+ * Create a new local es - invoked upon zebra notification.
+ */
+struct evpnes *bgp_evpn_es_new(struct bgp *bgp,
+                              esi_t *esi,
+                              struct ipaddr *originator_ip)
+{
+       char buf[100];
+       struct evpnes *es;
+
+       if (!bgp)
+               return NULL;
+
+       es = XCALLOC(MTYPE_BGP_EVPN_ES, sizeof(struct evpnes));
+       if (!es)
+               return NULL;
+
+       /* set the ESI and originator_ip */
+       memcpy(&es->esi, esi, sizeof(esi_t));
+       memcpy(&es->originator_ip, originator_ip, sizeof(struct ipaddr));
+
+       /* Initialise the VTEP list */
+       es->vtep_list = list_new();
+       es->vtep_list->cmp = (int (*)(void *, void *))evpn_vtep_ip_cmp;
+
+       /* auto derive RD for this es */
+       bf_assign_index(bm->rd_idspace, es->rd_id);
+       es->prd.family = AF_UNSPEC;
+       es->prd.prefixlen = 64;
+       sprintf(buf, "%s:%hu", inet_ntoa(bgp->router_id), es->rd_id);
+       (void)str2prefix_rd(buf, &es->prd);
+
+       /* Initialize the ES route table */
+       es->route_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN);
+
+       /* Add to hash */
+       if (!hash_get(bgp->esihash, es, hash_alloc_intern)) {
+               XFREE(MTYPE_BGP_EVPN_ES, es);
+               return NULL;
+       }
+
+       QOBJ_REG(es, evpnes);
+       return es;
+}
+
+/*
+ * Free a given ES -
+ * This just frees appropriate memory, caller should have taken other
+ * needed actions.
+ */
+void bgp_evpn_es_free(struct bgp *bgp, struct evpnes *es)
+{
+       list_delete_and_null(&es->vtep_list);
+       bgp_table_unlock(es->route_table);
+       bf_release_index(bm->rd_idspace, es->rd_id);
+       hash_release(bgp->esihash, es);
+       QOBJ_UNREG(es);
+       XFREE(MTYPE_BGP_EVPN_ES, es);
+}
+
+/*
+ * Import evpn route from global table to VNI/VRF/ESI.
  */
 int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi,
                          struct prefix *p, struct bgp_info *ri)
@@ -4157,7 +5112,7 @@ int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi,
 }
 
 /*
- * Unimport route from matching VNI(s).
+ * Unimport evpn route from VNI/VRF/ESI.
  */
 int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi,
                            struct prefix *p, struct bgp_info *ri)
@@ -4239,11 +5194,6 @@ int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
        struct bgpevpn *vpn;
        struct prefix_evpn p;
 
-       if (!bgp->vnihash) {
-               zlog_err("%u: VNI hash not created", bgp->vrf_id);
-               return -1;
-       }
-
        /* Lookup VNI hash - should exist. */
        vpn = bgp_evpn_lookup_vni(bgp, vni);
        if (!vpn || !is_vni_live(vpn)) {
@@ -4268,11 +5218,6 @@ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
        struct bgpevpn *vpn;
        struct prefix_evpn p;
 
-       if (!bgp->vnihash) {
-               zlog_err("%u: VNI hash not created", bgp->vrf_id);
-               return -1;
-       }
-
        /* Lookup VNI hash - should exist. */
        vpn = bgp_evpn_lookup_vni(bgp, vni);
        if (!vpn || !is_vni_live(vpn)) {
@@ -4287,7 +5232,8 @@ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
                char buf[ETHER_ADDR_STRLEN];
                char buf2[INET6_ADDRSTRLEN];
 
-               zlog_err(
+               flog_err(
+                       BGP_ERR_EVPN_ROUTE_CREATE,
                        "%u:Failed to create Type-2 route, VNI %u %s MAC %s IP %s (flags: 0x%x)",
                        bgp->vrf_id, vpn->vni,
                        CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY)
@@ -4304,13 +5250,12 @@ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
 static void link_l2vni_hash_to_l3vni(struct hash_backet *backet,
                                     struct bgp *bgp_vrf)
 {
-       struct bgpevpn *vpn = NULL;
+       struct bgpevpn *vpn = (struct bgpevpn *)backet->data;
        struct bgp *bgp_def = NULL;
 
        bgp_def = bgp_get_default();
        assert(bgp_def);
 
-       vpn = (struct bgpevpn *)backet->data;
        if (vpn->tenant_vrf_id == bgp_vrf->vrf_id)
                bgpevpn_link_to_l3vni(vpn);
 }
@@ -4324,12 +5269,13 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, struct ethaddr *rmac,
        struct bgpevpn *vpn = NULL;
        as_t as = 0;
 
-       /* get the default instamce - required to get the AS number for VRF
+       /* get the default instance - required to get the AS number for VRF
         * auto-creatio
         */
        bgp_def = bgp_get_default();
        if (!bgp_def) {
-               zlog_err(
+               flog_err(
+                       BGP_ERR_NO_DFLT,
                        "Cannot process L3VNI  %u ADD - default BGP instance not yet created",
                        l3vni);
                return -1;
@@ -4346,13 +5292,16 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, struct ethaddr *rmac,
                              BGP_INSTANCE_TYPE_VRF);
                switch (ret) {
                case BGP_ERR_MULTIPLE_INSTANCE_NOT_SET:
-                       zlog_err("'bgp multiple-instance' not present\n");
+                       flog_err(BGP_ERR_MULTI_INSTANCE,
+                                 "'bgp multiple-instance' not present\n");
                        return -1;
                case BGP_ERR_AS_MISMATCH:
-                       zlog_err("BGP is already running; AS is %u\n", as);
+                       flog_err(BGP_ERR_EVPN_AS_MISMATCH,
+                                 "BGP is already running; AS is %u\n", as);
                        return -1;
                case BGP_ERR_INSTANCE_MISMATCH:
-                       zlog_err("BGP instance name and AS number mismatch\n");
+                       flog_err(BGP_ERR_EVPN_INSTANCE_MISMATCH,
+                                 "BGP instance name and AS number mismatch\n");
                        return -1;
                }
 
@@ -4413,7 +5362,8 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, vrf_id_t vrf_id)
 
        bgp_vrf = bgp_lookup_by_vrf_id(vrf_id);
        if (!bgp_vrf) {
-               zlog_err(
+               flog_err(
+                       BGP_ERR_NO_DFLT,
                        "Cannot process L3VNI %u Del - Could not find BGP instance",
                        l3vni);
                return -1;
@@ -4421,17 +5371,19 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, vrf_id_t vrf_id)
 
        bgp_def = bgp_get_default();
        if (!bgp_def) {
-               zlog_err(
+               flog_err(
+                       BGP_ERR_NO_DFLT,
                        "Cannot process L3VNI %u Del - Could not find default BGP instance",
                        l3vni);
                return -1;
        }
 
-       /* unimport remote routes from VRF, if it is AUTO vrf bgp_delete will
-        * take care of uninstalling the routes from zebra
+       /* Remove remote routes from BGT VRF even if BGP_VRF_AUTO is configured,
+        * bgp_delete would not remove/decrement bgp_info of the ip_prefix
+        * routes. This will uninstalling the routes from zebra and decremnt the
+        * bgp info count.
         */
-       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))
-               uninstall_routes_for_vrf(bgp_vrf);
+       uninstall_routes_for_vrf(bgp_vrf);
 
        /* delete/withdraw all type-5 routes */
        delete_withdraw_vrf_routes(bgp_vrf);
@@ -4443,11 +5395,11 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, vrf_id_t vrf_id)
        memset(&bgp_vrf->rmac, 0, sizeof(struct ethaddr));
 
        /* delete RD/RT */
-       if (bgp_vrf->vrf_import_rtl && !list_isempty(bgp_vrf->vrf_import_rtl)) {
+       if (!list_isempty(bgp_vrf->vrf_import_rtl)) {
                bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
                list_delete_all_node(bgp_vrf->vrf_import_rtl);
        }
-       if (bgp_vrf->vrf_export_rtl && !list_isempty(bgp_vrf->vrf_export_rtl)) {
+       if (!list_isempty(bgp_vrf->vrf_export_rtl)) {
                list_delete_all_node(bgp_vrf->vrf_export_rtl);
        }
 
@@ -4477,11 +5429,6 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni)
 {
        struct bgpevpn *vpn;
 
-       if (!bgp->vnihash) {
-               zlog_err("%u: VNI hash not created", bgp->vrf_id);
-               return -1;
-       }
-
        /* Locate VNI hash */
        vpn = bgp_evpn_lookup_vni(bgp, vni);
        if (!vpn) {
@@ -4520,11 +5467,6 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
        struct bgpevpn *vpn;
        struct prefix_evpn p;
 
-       if (!bgp->vnihash) {
-               zlog_err("%u: VNI hash not created", bgp->vrf_id);
-               return -1;
-       }
-
        /* Lookup VNI. If present and no change, exit. */
        vpn = bgp_evpn_lookup_vni(bgp, vni);
        if (vpn) {
@@ -4560,7 +5502,8 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
        if (!vpn) {
                vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id);
                if (!vpn) {
-                       zlog_err(
+                       flog_err(
+                               BGP_ERR_VNI,
                                "%u: Failed to allocate VNI entry for VNI %u - at Add",
                                bgp->vrf_id, vni);
                        return -1;
@@ -4583,8 +5526,9 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
        /* Create EVPN type-3 route and schedule for processing. */
        build_evpn_type3_prefix(&p, vpn->originator_ip);
        if (update_evpn_route(bgp, vpn, &p, 0)) {
-               zlog_err("%u: Type3 route creation failure for VNI %u",
-                        bgp->vrf_id, vni);
+               flog_err(BGP_ERR_EVPN_ROUTE_CREATE,
+                         "%u: Type3 route creation failure for VNI %u",
+                         bgp->vrf_id, vni);
                return -1;
        }
 
@@ -4601,6 +5545,88 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
        return 0;
 }
 
+/*
+ * bgp_evpn_local_es_del
+ */
+int bgp_evpn_local_es_del(struct bgp *bgp,
+                         esi_t *esi,
+                         struct ipaddr *originator_ip)
+{
+       char buf[ESI_STR_LEN];
+       struct evpnes *es = NULL;
+
+       if (!bgp->esihash) {
+               flog_err(BGP_ERR_ES_CREATE, "%u: ESI hash not yet created",
+                         bgp->vrf_id);
+               return -1;
+       }
+
+       /* Lookup ESI hash - should exist. */
+       es = bgp_evpn_lookup_es(bgp, esi);
+       if (!es) {
+               zlog_warn("%u: ESI hash entry for ESI %s at Local ES DEL",
+                         bgp->vrf_id,
+                         esi_to_str(esi, buf, sizeof(buf)));
+               return -1;
+       }
+
+       /* Delete all local EVPN ES routes from ESI table
+        * and schedule for processing (to withdraw from peers))
+        */
+       delete_routes_for_es(bgp, es);
+
+       /* free the hash entry */
+       bgp_evpn_es_free(bgp, es);
+
+       return 0;
+}
+
+/*
+ * bgp_evpn_local_es_add
+ */
+int bgp_evpn_local_es_add(struct bgp *bgp,
+                         esi_t *esi,
+                         struct ipaddr *originator_ip)
+{
+       char buf[ESI_STR_LEN];
+       struct evpnes *es = NULL;
+       struct prefix_evpn p;
+
+       if (!bgp->esihash) {
+               flog_err(BGP_ERR_ES_CREATE, "%u: ESI hash not yet created",
+                         bgp->vrf_id);
+               return -1;
+       }
+
+       /* create the new es */
+       es = bgp_evpn_lookup_es(bgp, esi);
+       if (!es) {
+               es = bgp_evpn_es_new(bgp, esi, originator_ip);
+               if (!es) {
+                       flog_err(
+                               BGP_ERR_ES_CREATE,
+                               "%u: Failed to allocate ES entry for ESI %s - at Local ES Add",
+                               bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf)));
+                       return -1;
+               }
+       }
+       UNSET_FLAG(es->flags, EVPNES_REMOTE);
+       SET_FLAG(es->flags, EVPNES_LOCAL);
+
+       build_evpn_type4_prefix(&p, esi, originator_ip->ipaddr_v4);
+       if (update_evpn_type4_route(bgp, es, &p)) {
+               flog_err(BGP_ERR_EVPN_ROUTE_CREATE,
+                         "%u: Type4 route creation failure for ESI %s",
+                         bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf)));
+               return -1;
+       }
+
+       /* import all remote ES routes in th ES table */
+       install_routes_for_es(bgp, es);
+
+       return 0;
+}
+
 /*
  * Cleanup EVPN information on disable - Need to delete and withdraw
  * EVPN routes from peers.
@@ -4618,25 +5644,25 @@ void bgp_evpn_cleanup_on_disable(struct bgp *bgp)
  */
 void bgp_evpn_cleanup(struct bgp *bgp)
 {
-       if (bgp->vnihash)
-               hash_iterate(bgp->vnihash, (void (*)(struct hash_backet *,
-                                                    void *))free_vni_entry,
-                            bgp);
-       if (bgp->import_rt_hash)
-               hash_free(bgp->import_rt_hash);
+       hash_iterate(bgp->vnihash,
+                    (void (*)(struct hash_backet *, void *))free_vni_entry,
+                    bgp);
+
+       hash_free(bgp->import_rt_hash);
        bgp->import_rt_hash = NULL;
-       if (bgp->vrf_import_rt_hash)
-               hash_free(bgp->vrf_import_rt_hash);
+
+       hash_free(bgp->vrf_import_rt_hash);
        bgp->vrf_import_rt_hash = NULL;
-       if (bgp->vnihash)
-               hash_free(bgp->vnihash);
+
+       hash_free(bgp->vnihash);
        bgp->vnihash = NULL;
-       if (bgp->vrf_import_rtl)
-               list_delete_and_null(&bgp->vrf_import_rtl);
-       if (bgp->vrf_export_rtl)
-               list_delete_and_null(&bgp->vrf_export_rtl);
-       if (bgp->l2vnis)
-               list_delete_and_null(&bgp->l2vnis);
+       if (bgp->esihash)
+               hash_free(bgp->esihash);
+       bgp->esihash = NULL;
+
+       list_delete_and_null(&bgp->vrf_import_rtl);
+       list_delete_and_null(&bgp->vrf_export_rtl);
+       list_delete_and_null(&bgp->l2vnis);
 }
 
 /*
@@ -4649,6 +5675,9 @@ void bgp_evpn_init(struct bgp *bgp)
 {
        bgp->vnihash =
                hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash");
+       bgp->esihash =
+               hash_create(esi_hash_keymake, esi_cmp,
+                           "BGP EVPN Local ESI Hash");
        bgp->import_rt_hash =
                hash_create(import_rt_hash_key_make, import_rt_hash_cmp,
                            "BGP Import RT Hash");