]> git.puffer.fish Git - matthieu/frr.git/commitdiff
zebra: add workqueue support for EVPN updates
authorMark Stapp <mjs@voltanet.io>
Mon, 19 Apr 2021 15:12:51 +0000 (11:12 -0400)
committerMark Stapp <mjs.ietf@gmail.com>
Mon, 19 Jul 2021 14:36:12 +0000 (10:36 -0400)
Add workqueue subqueue for EVPN/VxLAN updates; migrate the
evpn route and remote ES processing from their ZAPI handlers
to the workqueue.

Signed-off-by: Mark Stapp <mjs@voltanet.io>
zebra/rib.h
zebra/zebra_evpn_mh.c
zebra/zebra_evpn_mh.h
zebra/zebra_evpn_neigh.c
zebra/zebra_rib.c

index b7ffb9ce8da558e80094e576e99f56bee9010922..6902e0881bc78ab9a7eb301367827e605f45ee2d 100644 (file)
@@ -177,15 +177,16 @@ struct route_entry {
 
 /* meta-queue structure:
  * sub-queue 0: nexthop group objects
- * sub-queue 1: connected
- * sub-queue 2: kernel
- * sub-queue 3: static
- * sub-queue 4: RIP, RIPng, OSPF, OSPF6, IS-IS, EIGRP, NHRP
- * sub-queue 5: iBGP, eBGP
- * sub-queue 6: any other origin (if any) typically those that
+ * sub-queue 1: EVPN/VxLAN objects
+ * sub-queue 2: connected
+ * sub-queue 3: kernel
+ * sub-queue 4: static
+ * sub-queue 5: RIP, RIPng, OSPF, OSPF6, IS-IS, EIGRP, NHRP
+ * sub-queue 6: iBGP, eBGP
+ * sub-queue 7: any other origin (if any) typically those that
  *              don't generate routes
  */
-#define MQ_SIZE 7
+#define MQ_SIZE 8
 struct meta_queue {
        struct list *subq[MQ_SIZE];
        uint32_t size; /* sum of lengths of all subqueues */
@@ -446,6 +447,21 @@ extern int rib_queue_nhg_ctx_add(struct nhg_ctx *ctx);
 /* Enqueue incoming nhg from proto daemon for processing */
 extern int rib_queue_nhe_add(struct nhg_hash_entry *nhe);
 
+/* Enqueue evpn route for processing */
+int zebra_rib_queue_evpn_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac,
+                                  const struct ipaddr *vtep_ip,
+                                  const struct prefix *host_prefix);
+int zebra_rib_queue_evpn_route_del(vrf_id_t vrf_id,
+                                  const struct ipaddr *vtep_ip,
+                                  const struct prefix *host_prefix);
+/* Enqueue EVPN remote ES for processing */
+int zebra_rib_queue_evpn_rem_es_add(const esi_t *esi,
+                                   const struct in_addr *vtep_ip,
+                                   bool esr_rxed, uint8_t df_alg,
+                                   uint16_t df_pref);
+int zebra_rib_queue_evpn_rem_es_del(const esi_t *esi,
+                                   const struct in_addr *vtep_ip);
+
 extern void meta_queue_free(struct meta_queue *mq);
 extern int zebra_rib_labeled_unicast(struct route_entry *re);
 extern struct route_table *rib_table_ipv6;
index d6ae92a03d2bedab0dd67209174e48870d4f72fc..eb57338a8eca566c564e4194bc41ab15e3726f61 100644 (file)
@@ -1747,7 +1747,7 @@ static int zebra_es_rb_cmp(const struct zebra_evpn_es *es1,
 RB_GENERATE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp);
 
 /* Lookup ES */
-struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi)
+struct zebra_evpn_es *zebra_evpn_es_find(const esi_t *esi)
 {
        struct zebra_evpn_es tmp;
 
@@ -1758,7 +1758,7 @@ struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi)
 /* A new local es is created when a local-es-id and sysmac is configured
  * against an interface.
  */
-static struct zebra_evpn_es *zebra_evpn_es_new(esi_t *esi)
+static struct zebra_evpn_es *zebra_evpn_es_new(const esi_t *esi)
 {
        struct zebra_evpn_es *es;
 
@@ -2392,7 +2392,7 @@ static int zebra_evpn_type3_esi_update(struct zebra_if *zif, uint32_t lid,
        return zebra_evpn_local_es_update(zif, &esi);
 }
 
-static int zebra_evpn_remote_es_del(esi_t *esi, struct in_addr vtep_ip)
+int zebra_evpn_remote_es_del(const esi_t *esi, struct in_addr vtep_ip)
 {
        char buf[ESI_STR_LEN];
        struct zebra_evpn_es *es;
@@ -2432,9 +2432,8 @@ static void zebra_evpn_remote_es_flush(struct zebra_evpn_es **esp)
        zebra_evpn_es_remote_info_re_eval(esp);
 }
 
-static int zebra_evpn_remote_es_add(esi_t *esi, struct in_addr vtep_ip,
-                                   bool esr_rxed, uint8_t df_alg,
-                                   uint16_t df_pref)
+int zebra_evpn_remote_es_add(const esi_t *esi, struct in_addr vtep_ip,
+                            bool esr_rxed, uint8_t df_alg, uint16_t df_pref)
 {
        char buf[ESI_STR_LEN];
        struct zebra_evpn_es *es;
@@ -2498,10 +2497,10 @@ void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS)
                                                                     : false;
                STREAM_GETC(s, df_alg);
                STREAM_GETW(s, df_pref);
-               zebra_evpn_remote_es_add(&esi, vtep_ip, esr_rxed, df_alg,
-                                        df_pref);
+               zebra_rib_queue_evpn_rem_es_add(&esi, &vtep_ip, esr_rxed, df_alg,
+                                               df_pref);
        } else {
-               zebra_evpn_remote_es_del(&esi, vtep_ip);
+               zebra_rib_queue_evpn_rem_es_del(&esi, &vtep_ip);
        }
 
 stream_failure:
@@ -3901,12 +3900,12 @@ void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS)
                if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
                        zlog_debug("evpn remote nh %d %pIA rmac %pEA add",
                                   vrf_id, &nh, &rmac);
-               zebra_vxlan_evpn_vrf_route_add(vrf_id, &rmac, &nh,
+               zebra_rib_queue_evpn_route_add(vrf_id, &rmac, &nh,
                                               (struct prefix *)&dummy_prefix);
        } else {
                if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
                        zlog_debug("evpn remote nh %d %pIA del", vrf_id, &nh);
-               zebra_vxlan_evpn_vrf_route_del(vrf_id, &nh,
+               zebra_rib_queue_evpn_route_del(vrf_id, &nh,
                                               (struct prefix *)&dummy_prefix);
        }
 }
index 8861e80cee4542e692f2e8bfce8800cf38e5c780..8bb9e0280255c9d9c60926732847bd3625632262 100644 (file)
@@ -330,6 +330,9 @@ extern void zebra_evpn_es_show_detail(struct vty *vty, bool uj);
 extern void zebra_evpn_es_show_esi(struct vty *vty, bool uj, esi_t *esi);
 extern void zebra_evpn_update_all_es(zebra_evpn_t *zevpn);
 extern void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS);
+int zebra_evpn_remote_es_add(const esi_t *esi, struct in_addr vtep_ip,
+                            bool esr_rxed, uint8_t df_alg, uint16_t df_pref);
+int zebra_evpn_remote_es_del(const esi_t *esi, struct in_addr vtep_ip);
 extern void zebra_evpn_es_evi_show(struct vty *vty, bool uj, int detail);
 extern void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj,
                vni_t vni, int detail);
@@ -337,7 +340,7 @@ extern void zebra_evpn_es_mac_deref_entry(zebra_mac_t *mac);
 extern bool zebra_evpn_es_mac_ref_entry(zebra_mac_t *mac,
                struct zebra_evpn_es *es);
 extern bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi);
-extern struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi);
+extern struct zebra_evpn_es *zebra_evpn_es_find(const esi_t *esi);
 extern void zebra_evpn_interface_init(void);
 extern int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp);
 extern void zebra_evpn_acc_vl_show(struct vty *vty, bool uj);
index a78bac010c12086155e104f46fabdbb2f3b08345..e58144d0c90876a12592aafa6739457f6822eae4 100644 (file)
@@ -2299,7 +2299,7 @@ int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip)
        }
 
        /* If it is a remote entry, the kernel has aged this out or someone has
-        * deleted it, it needs to be re-installed as Quagga is the owner.
+        * deleted it, it needs to be re-installed as FRR is the owner.
         */
        if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
                zebra_evpn_rem_neigh_install(zevpn, n, false /*was_static*/);
index 12cc0b4e8a85663d57c67dc9df2010a978098acd..c02dc6aa680d0c154e77620bd8fb07b26a82ccf1 100644 (file)
 #include "zebra/zebra_vxlan.h"
 #include "zebra/zapi_msg.h"
 #include "zebra/zebra_dplane.h"
+#include "zebra/zebra_evpn_mh.h"
 
 DEFINE_MGROUP(ZEBRA, "zebra");
 
 DEFINE_MTYPE(ZEBRA, RE,       "Route Entry");
 DEFINE_MTYPE_STATIC(ZEBRA, RIB_DEST,       "RIB destination");
 DEFINE_MTYPE_STATIC(ZEBRA, RIB_UPDATE_CTX, "Rib update context object");
-DEFINE_MTYPE_STATIC(ZEBRA, WQ_NHG_WRAPPER, "WQ nhg wrapper");
+DEFINE_MTYPE_STATIC(ZEBRA, WQ_WRAPPER, "WQ wrapper");
 
 /*
  * Event, list, and mutex for delivery of dataplane results
@@ -74,7 +75,7 @@ static struct dplane_ctx_q rib_dplane_q;
 DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason),
            (rn, reason));
 
-/* Should we allow non Quagga processes to delete our routes */
+/* Should we allow non FRR processes to delete our routes */
 extern int allow_delete;
 
 /* Each route type's string and default distance value. */
@@ -83,41 +84,44 @@ static const struct {
        uint8_t distance;
        uint8_t meta_q_map;
 } route_info[ZEBRA_ROUTE_MAX] = {
-       [ZEBRA_ROUTE_NHG] = {ZEBRA_ROUTE_NHG, 255 /* Uneeded for nhg's */, 0},
-       [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, 6},
-       [ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, 0, 2},
-       [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, 0, 1},
-       [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, 1, 3},
-       [ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, 120, 4},
-       [ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, 120, 4},
-       [ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, 110, 4},
-       [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110, 4},
-       [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115, 4},
-       [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */, 5},
-       [ZEBRA_ROUTE_PIM] = {ZEBRA_ROUTE_PIM, 255, 6},
-       [ZEBRA_ROUTE_EIGRP] = {ZEBRA_ROUTE_EIGRP, 90, 4},
-       [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10, 4},
-       [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 255, 6},
-       [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 255, 6},
-       [ZEBRA_ROUTE_TABLE] = {ZEBRA_ROUTE_TABLE, 150, 3},
-       [ZEBRA_ROUTE_LDP] = {ZEBRA_ROUTE_LDP, 150, 6},
-       [ZEBRA_ROUTE_VNC] = {ZEBRA_ROUTE_VNC, 20, 5},
-       [ZEBRA_ROUTE_VNC_DIRECT] = {ZEBRA_ROUTE_VNC_DIRECT, 20, 5},
-       [ZEBRA_ROUTE_VNC_DIRECT_RH] = {ZEBRA_ROUTE_VNC_DIRECT_RH, 20, 5},
-       [ZEBRA_ROUTE_BGP_DIRECT] = {ZEBRA_ROUTE_BGP_DIRECT, 20, 5},
-       [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, 5},
-       [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 4},
-       [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 6},
-       [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 6},
-       [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 6},
-       [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 4},
-       [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 6},
-       [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, 255, 6},
+       [ZEBRA_ROUTE_NHG] = {ZEBRA_ROUTE_NHG, 255 /* Unneeded for nhg's */, 0},
+       [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, 7},
+       [ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, 0, 3},
+       [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, 0, 2},
+       [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, 1, 4},
+       [ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, 120, 5},
+       [ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, 120, 5},
+       [ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, 110, 5},
+       [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110, 5},
+       [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115, 5},
+       [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */, 6},
+       [ZEBRA_ROUTE_PIM] = {ZEBRA_ROUTE_PIM, 255, 7},
+       [ZEBRA_ROUTE_EIGRP] = {ZEBRA_ROUTE_EIGRP, 90, 5},
+       [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10, 5},
+       [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 255, 7},
+       [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 255, 7},
+       [ZEBRA_ROUTE_TABLE] = {ZEBRA_ROUTE_TABLE, 150, 4},
+       [ZEBRA_ROUTE_LDP] = {ZEBRA_ROUTE_LDP, 150, 7},
+       [ZEBRA_ROUTE_VNC] = {ZEBRA_ROUTE_VNC, 20, 6},
+       [ZEBRA_ROUTE_VNC_DIRECT] = {ZEBRA_ROUTE_VNC_DIRECT, 20, 6},
+       [ZEBRA_ROUTE_VNC_DIRECT_RH] = {ZEBRA_ROUTE_VNC_DIRECT_RH, 20, 6},
+       [ZEBRA_ROUTE_BGP_DIRECT] = {ZEBRA_ROUTE_BGP_DIRECT, 20, 6},
+       [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, 6},
+       [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 5},
+       [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 7},
+       [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 7},
+       [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 7},
+       [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 5},
+       [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 7},
+       [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, 255, 7},
        /* Any new route type added to zebra, should be mirrored here */
 
        /* no entry/default: 150 */
 };
 
+/* EVPN/VXLAN subqueue is number 1 */
+#define META_QUEUE_EVPN 1
+
 /* Wrapper struct for nhg workqueue items; a 'ctx' is an incoming update
  * from the OS, and an 'nhe' is a nhe update.
  */
@@ -132,6 +136,23 @@ struct wq_nhg_wrapper {
 #define WQ_NHG_WRAPPER_TYPE_CTX  0x01
 #define WQ_NHG_WRAPPER_TYPE_NHG  0x02
 
+/* Wrapper structs for evpn/vxlan workqueue items. */
+struct wq_evpn_wrapper {
+       int type;
+       bool add_p;
+       vrf_id_t vrf_id;
+       bool esr_rxed;
+       uint8_t df_alg;
+       uint16_t df_pref;
+       esi_t esi;
+       struct ipaddr ip;
+       struct ethaddr mac;
+       struct prefix prefix;
+};
+
+#define WQ_EVPN_WRAPPER_TYPE_VRFROUTE 0x01
+#define WQ_EVPN_WRAPPER_TYPE_REM_ES   0x02
+
 /* %pRN is already a printer for route_nodes that just prints the prefix */
 #ifdef _FRR_ATTRIBUTE_PRINTFRR
 #pragma FRR printfrr_ext "%pZN" (struct route_node *)
@@ -2315,6 +2336,39 @@ done:
        dplane_ctx_fini(&ctx);
 }
 
+/*
+ * Process a node from the EVPN/VXLAN subqueue.
+ */
+static void process_subq_evpn(struct listnode *lnode)
+{
+       struct wq_evpn_wrapper *w;
+
+       /* In general, the list node points to a wrapper object
+        * holding the info necessary to make some update.
+        */
+       w = listgetdata(lnode);
+       if (!w)
+               return;
+
+       if (w->type == WQ_EVPN_WRAPPER_TYPE_VRFROUTE) {
+               if (w->add_p)
+                       zebra_vxlan_evpn_vrf_route_add(w->vrf_id, &w->mac,
+                                                      &w->ip, &w->prefix);
+               else
+                       zebra_vxlan_evpn_vrf_route_del(w->vrf_id, &w->ip,
+                                                      &w->prefix);
+       } else if (w->type == WQ_EVPN_WRAPPER_TYPE_REM_ES) {
+               if (w->add_p)
+                       zebra_evpn_remote_es_add(&w->esi, w->ip.ipaddr_v4,
+                                                w->esr_rxed, w->df_alg,
+                                                w->df_pref);
+               else
+                       zebra_evpn_remote_es_del(&w->esi, w->ip.ipaddr_v4);
+       }
+
+       XFREE(MTYPE_WQ_WRAPPER, w);
+}
+
 /*
  * Process the nexthop-group workqueue subqueue
  */
@@ -2355,8 +2409,7 @@ static void process_subq_nhg(struct listnode *lnode)
                /* Process incoming nhg update, probably from a proto daemon */
                newnhe = zebra_nhg_proto_add(nhe->id, nhe->type,
                                             nhe->zapi_instance,
-                                            nhe->zapi_session,
-                                            &nhe->nhg, 0);
+                                            nhe->zapi_session, &nhe->nhg, 0);
 
                /* Report error to daemon via ZAPI */
                if (newnhe == NULL)
@@ -2368,7 +2421,7 @@ static void process_subq_nhg(struct listnode *lnode)
                zebra_nhg_free(nhe);
        }
 
-       XFREE(MTYPE_WQ_NHG_WRAPPER, w);
+       XFREE(MTYPE_WQ_WRAPPER, w);
 }
 
 static void process_subq_route(struct listnode *lnode, uint8_t qindex)
@@ -2411,9 +2464,9 @@ static void process_subq_route(struct listnode *lnode, uint8_t qindex)
        route_unlock_node(rnode);
 }
 
-/* Take a list of route_node structs and return 1, if there was a record
- * picked from it and processed by rib_process(). Don't process more,
- * than one RN record; operate only in the specified sub-queue.
+/*
+ * Examine the specified subqueue; process one entry and return 1 if
+ * there is a node, return 0 otherwise.
  */
 static unsigned int process_subq(struct list *subq, uint8_t qindex)
 {
@@ -2422,7 +2475,9 @@ static unsigned int process_subq(struct list *subq, uint8_t qindex)
        if (!lnode)
                return 0;
 
-       if (qindex == route_info[ZEBRA_ROUTE_NHG].meta_q_map)
+       if (qindex == META_QUEUE_EVPN)
+               process_subq_evpn(lnode);
+       else if (qindex == route_info[ZEBRA_ROUTE_NHG].meta_q_map)
                process_subq_nhg(lnode);
        else
                process_subq_route(lnode, qindex);
@@ -2432,7 +2487,7 @@ static unsigned int process_subq(struct list *subq, uint8_t qindex)
        return 1;
 }
 
-/* Dispatch the meta queue by picking, processing and unlocking the next RN from
+/* Dispatch the meta queue by picking and processing the next node from
  * a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and
  * data is pointed to the meta queue structure.
  */
@@ -2538,7 +2593,7 @@ static int rib_meta_queue_nhg_ctx_add(struct meta_queue *mq, void *data)
        if (!ctx)
                return -1;
 
-       w = XCALLOC(MTYPE_WQ_NHG_WRAPPER, sizeof(struct wq_nhg_wrapper));
+       w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_nhg_wrapper));
 
        w->type = WQ_NHG_WRAPPER_TYPE_CTX;
        w->u.ctx = ctx;
@@ -2564,7 +2619,7 @@ static int rib_meta_queue_nhg_add(struct meta_queue *mq, void *data)
        if (!nhe)
                return -1;
 
-       w = XCALLOC(MTYPE_WQ_NHG_WRAPPER, sizeof(struct wq_nhg_wrapper));
+       w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_nhg_wrapper));
 
        w->type = WQ_NHG_WRAPPER_TYPE_NHG;
        w->u.nhe = nhe;
@@ -2579,6 +2634,14 @@ static int rib_meta_queue_nhg_add(struct meta_queue *mq, void *data)
        return 0;
 }
 
+static int rib_meta_queue_evpn_add(struct meta_queue *mq, void *data)
+{
+       listnode_add(mq->subq[META_QUEUE_EVPN], data);
+       mq->size++;
+
+       return 0;
+}
+
 static int mq_add_handler(void *data,
                          int (*mq_add_func)(struct meta_queue *mq, void *data))
 {
@@ -2640,6 +2703,123 @@ int rib_queue_nhe_add(struct nhg_hash_entry *nhe)
        return mq_add_handler(nhe, rib_meta_queue_nhg_add);
 }
 
+/*
+ * Enqueue evpn route for processing
+ */
+int zebra_rib_queue_evpn_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac,
+                                  const struct ipaddr *vtep_ip,
+                                  const struct prefix *host_prefix)
+{
+       struct wq_evpn_wrapper *w;
+
+       w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+       w->type = WQ_EVPN_WRAPPER_TYPE_VRFROUTE;
+       w->add_p = true;
+       w->vrf_id = vrf_id;
+       w->mac = *rmac;
+       w->ip = *vtep_ip;
+       w->prefix = *host_prefix;
+
+       if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+               zlog_debug("%s: (%u)%pIA, host prefix %pFX enqueued", __func__,
+                          vrf_id, vtep_ip, host_prefix);
+
+       return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+int zebra_rib_queue_evpn_route_del(vrf_id_t vrf_id,
+                                  const struct ipaddr *vtep_ip,
+                                  const struct prefix *host_prefix)
+{
+       struct wq_evpn_wrapper *w;
+
+       w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+       w->type = WQ_EVPN_WRAPPER_TYPE_VRFROUTE;
+       w->add_p = false;
+       w->vrf_id = vrf_id;
+       w->ip = *vtep_ip;
+       w->prefix = *host_prefix;
+
+       if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+               zlog_debug("%s: (%u)%pIA, host prefix %pFX enqueued", __func__,
+                          vrf_id, vtep_ip, host_prefix);
+
+       return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+/* Enqueue EVPN remote ES for processing */
+int zebra_rib_queue_evpn_rem_es_add(const esi_t *esi,
+                                   const struct in_addr *vtep_ip,
+                                   bool esr_rxed, uint8_t df_alg,
+                                   uint16_t df_pref)
+{
+       struct wq_evpn_wrapper *w;
+       char buf[ESI_STR_LEN];
+
+       w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+       w->type = WQ_EVPN_WRAPPER_TYPE_REM_ES;
+       w->add_p = true;
+       w->esi = *esi;
+       w->ip.ipa_type = IPADDR_V4;
+       w->ip.ipaddr_v4 = *vtep_ip;
+       w->esr_rxed = esr_rxed;
+       w->df_alg = df_alg;
+       w->df_pref = df_pref;
+
+       if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+               zlog_debug("%s: vtep %pI4, esi %s enqueued", __func__, vtep_ip,
+                          esi_to_str(esi, buf, sizeof(buf)));
+
+       return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+int zebra_rib_queue_evpn_rem_es_del(const esi_t *esi,
+                                   const struct in_addr *vtep_ip)
+{
+       struct wq_evpn_wrapper *w;
+       char buf[ESI_STR_LEN];
+
+       w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+       w->type = WQ_EVPN_WRAPPER_TYPE_REM_ES;
+       w->add_p = false;
+       w->esi = *esi;
+       w->ip.ipa_type = IPADDR_V4;
+       w->ip.ipaddr_v4 = *vtep_ip;
+
+       if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+               if (memcmp(esi, zero_esi, sizeof(esi_t)) != 0)
+                       esi_to_str(esi, buf, sizeof(buf));
+               else
+                       strlcpy(buf, "-", sizeof(buf));
+
+               zlog_debug("%s: vtep %pI4, esi %s enqueued", __func__, vtep_ip,
+                          buf);
+       }
+
+       return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+/* Clean up the EVPN meta-queue list */
+static void evpn_meta_queue_free(struct list *l)
+{
+       struct listnode *node;
+       struct wq_evpn_wrapper *w;
+
+       /* Free the node wrapper object, and the struct it wraps */
+       while ((node = listhead(l)) != NULL) {
+               w = node->data;
+               node->data = NULL;
+
+               XFREE(MTYPE_WQ_WRAPPER, w);
+
+               list_delete_node(l, node);
+       }
+}
+
 /* Clean up the nhg meta-queue list */
 static void nhg_meta_queue_free(struct list *l)
 {
@@ -2656,7 +2836,7 @@ static void nhg_meta_queue_free(struct list *l)
                else if (w->type == WQ_NHG_WRAPPER_TYPE_NHG)
                        zebra_nhg_free(w->u.nhe);
 
-               XFREE(MTYPE_WQ_NHG_WRAPPER, w);
+               XFREE(MTYPE_WQ_WRAPPER, w);
 
                list_delete_node(l, node);
        }
@@ -2688,6 +2868,8 @@ void meta_queue_free(struct meta_queue *mq)
                /* Some subqueues may need cleanup - nhgs for example */
                if (i == route_info[ZEBRA_ROUTE_NHG].meta_q_map)
                        nhg_meta_queue_free(mq->subq[i]);
+               else if (i == META_QUEUE_EVPN)
+                       evpn_meta_queue_free(mq->subq[i]);
 
                list_delete(&mq->subq[i]);
        }
@@ -2763,7 +2945,7 @@ rib_dest_t *zebra_rib_create_dest(struct route_node *rn)
  * dest is created on-demand by rib_link() and is kept around at least
  * as long as there are ribs hanging off it (@see rib_gc_dest()).
  *
- * Refcounting (aka "locking" throughout the GNU Zebra and Quagga code):
+ * Refcounting (aka "locking" throughout the Zebra and FRR code):
  *
  * - route_nodes: refcounted by:
  *   - dest attached to route_node:
@@ -3518,7 +3700,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
                                               &(tmp_nh->gate.ipv6),
                                               sizeof(struct in6_addr));
                                }
-                               zebra_vxlan_evpn_vrf_route_del(re->vrf_id,
+                               zebra_rib_queue_evpn_route_del(re->vrf_id,
                                                               &vtep_ip, p);
                        }
                }