]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: L3NHG infrastructure for host routes in EVPN
authorAnuradha Karuppiah <anuradhak@cumulusnetworks.com>
Sat, 9 May 2020 02:24:56 +0000 (19:24 -0700)
committerAnuradha Karuppiah <anuradhak@nvidia.com>
Tue, 24 Nov 2020 19:06:08 +0000 (11:06 -0800)
ES-VRF entries are maintained for the purpose of L3-NHG creation -
1. Each ES-EVI entry is associated with a tenant VRF. This associaton
triggers the creation of an ES-VRF entry.
2. Type-2/MAC-IP routes are imported into a tenant VRF and programmed as
a /32 or host route entry in the dataplane. If the destination of
the host route is a remote-ES the route is programmed with the
corresponding (keyed in by {vrf,ES-id}) L3-NHG.
3. The reason for this indirection (route->L3-NHG, L3-NHG->list-of-VTEPs)
is to avoid route updates to the dplane when a remote-ES link flaps i.e.
instead of updating all the dependent routes the NHG's contents are
updated. This reduces the amount of dataplane updates (fewer nhg updates vs.
route updates) allowing for a faster failover.

Signed-off-by: Anuradha Karuppiah <anuradhak@cumulusnetworks.com>
bgpd/bgp_evpn_mh.c
bgpd/bgp_evpn_mh.h
bgpd/bgp_evpn_private.h
bgpd/bgp_main.c
bgpd/bgp_memory.c
bgpd/bgp_memory.h
bgpd/bgp_nht.c
bgpd/bgp_nht.h
bgpd/bgpd.c
bgpd/bgpd.h

index bf9a2f849ad38b299635448a4b52062235d161ae..39523ea206d669cf27a95752877e1dca84201db3 100644 (file)
@@ -48,6 +48,7 @@
 #include "bgpd/bgp_zebra.h"
 #include "bgpd/bgp_addpath.h"
 #include "bgpd/bgp_label.h"
+#include "bgpd/bgp_nht.h"
 
 static void bgp_evpn_local_es_down(struct bgp *bgp,
                struct bgp_evpn_es *es);
@@ -63,6 +64,8 @@ static void bgp_evpn_es_vtep_del(struct bgp *bgp,
 static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es);
 static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es);
 static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi);
+static uint32_t bgp_evpn_es_get_active_vtep_cnt(struct bgp_evpn_es *es);
+static void bgp_evpn_l3nhg_update_on_vtep_chg(struct bgp_evpn_es *es);
 
 esi_t zero_esi_buf, *zero_esi = &zero_esi_buf;
 
@@ -1228,6 +1231,9 @@ static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp,
                /* send remote ES to zebra */
                bgp_zebra_send_remote_es_vtep(bgp, es_vtep, new_active);
 
+               /* update L3NHG associated with the ES */
+               bgp_evpn_l3nhg_update_on_vtep_chg(es_vtep->es);
+
                /* queue up the es for background consistency checks */
                bgp_evpn_es_cons_checks_pend_add(es_vtep->es);
        }
@@ -1351,6 +1357,10 @@ static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi)
        es->es_evi_list = list_new();
        listset_app_node_mem(es->es_evi_list);
 
+       /* Initialise the ES-VRF list used for L3NHG management */
+       es->es_vrf_list = list_new();
+       listset_app_node_mem(es->es_vrf_list);
+
        QOBJ_REG(es, bgp_evpn_es);
 
        return es;
@@ -1370,6 +1380,7 @@ static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller)
 
        /* cleanup resources maintained against the ES */
        list_delete(&es->es_evi_list);
+       list_delete(&es->es_vrf_list);
        list_delete(&es->es_vtep_list);
        bgp_table_unlock(es->route_table);
 
@@ -1940,6 +1951,269 @@ void bgp_evpn_es_show_esi(struct vty *vty, esi_t *esi, bool uj)
        }
 }
 
+/*****************************************************************************/
+/* Ethernet Segment to VRF association -
+ * 1. Each ES-EVI entry is associated with a tenant VRF. This associaton
+ * triggers the creation of an ES-VRF entry.
+ * 2. The ES-VRF entry is maintained for the purpose of L3-NHG creation
+ * 3. Type-2/MAC-IP routes are imported into a tenant VRF and programmed as
+ * a /32 or host route entry in the dataplane. If the destination of
+ * the host route is a remote-ES the route is programmed with the
+ * corresponding (keyed in by {vrf,ES-id}) L3-NHG.
+ * 4. The reason for this indirection (route->L3-NHG, L3-NHG->list-of-VTEPs)
+ * is to avoid route updates to the dplane when a remote-ES link flaps i.e.
+ * instead of updating all the dependent routes the NHG's contents are updated.
+ * This reduces the amount of datplane updates (nhg updates vs. route updates)
+ * allowing for a faster failover.
+ *
+ * XXX - can the L3 SVI index change without change in vpn->bgp_vrf
+ * association? If yes we need to handle that by updating all the L3 NHGs
+ * in that VRF.
+ */
+/******************************** L3 NHG management *************************/
+static void bgp_evpn_l3nhg_zebra_add(struct bgp_evpn_es_vrf *es_vrf)
+{
+       uint32_t nh_cnt = 0;
+       struct listnode *node;
+       struct bgp_evpn_es_vtep *es_vtep;
+       struct bgp_evpn_es *es = es_vrf->es;
+
+       if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+               zlog_debug("es %s vrf %u nhg 0x%x to zebra", es->esi_str,
+                          es_vrf->bgp_vrf->vrf_id, es_vrf->nhg_id);
+       for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+               if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE)) {
+                       ++nh_cnt;
+                       if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+                               zlog_debug("nhg 0x%x vtep %pI4 dev 0x%x",
+                                          es_vrf->nhg_id, &es_vtep->vtep_ip,
+                                          es_vrf->bgp_vrf->l3vni_svi_ifindex);
+               }
+       }
+
+       /* XXX - program NHG in zebra */
+}
+
+static void bgp_evpn_l3nhg_zebra_del(struct bgp_evpn_es_vrf *es_vrf)
+{
+       if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+               zlog_debug("es %s vrf %u nhg 0x%x to zebra",
+                          es_vrf->es->esi_str, es_vrf->bgp_vrf->vrf_id,
+                          es_vrf->nhg_id);
+
+       /* XXX - program NHG in zebra */
+}
+
+static void bgp_evpn_l3nhg_deactivate(struct bgp_evpn_es_vrf *es_vrf)
+{
+       if (!(es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE))
+               return;
+
+       if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+               zlog_debug("es %s vrf %u nhg 0x%x de-activate",
+                          es_vrf->es->esi_str, es_vrf->bgp_vrf->vrf_id,
+                          es_vrf->nhg_id);
+       bgp_evpn_l3nhg_zebra_del(es_vrf);
+       es_vrf->flags &= ~BGP_EVPNES_VRF_NHG_ACTIVE;
+}
+
+static void bgp_evpn_l3nhg_activate(struct bgp_evpn_es_vrf *es_vrf, bool update)
+{
+       if (!bgp_evpn_es_get_active_vtep_cnt(es_vrf->es)) {
+               bgp_evpn_l3nhg_deactivate(es_vrf);
+               return;
+       }
+
+       if (es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE) {
+               if (!update)
+                       return;
+       } else {
+               if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+                       zlog_debug("es %s vrf %u nhg 0x%x activate",
+                                  es_vrf->es->esi_str, es_vrf->bgp_vrf->vrf_id,
+                                  es_vrf->nhg_id);
+               es_vrf->flags |= BGP_EVPNES_VRF_NHG_ACTIVE;
+       }
+
+       bgp_evpn_l3nhg_zebra_add(es_vrf);
+}
+
+/* when a VTEP is activated or de-activated against an ES associated
+ * VRFs' NHG needs to be updated
+ */
+static void bgp_evpn_l3nhg_update_on_vtep_chg(struct bgp_evpn_es *es)
+{
+       struct bgp_evpn_es_vrf *es_vrf;
+       struct listnode *es_vrf_node;
+
+       if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+               zlog_debug("es %s nhg update on vtep chg", es->esi_str);
+
+       for (ALL_LIST_ELEMENTS_RO(es->es_vrf_list, es_vrf_node, es_vrf))
+               bgp_evpn_l3nhg_activate(es_vrf, true /* update */);
+}
+
+/* compare ES-IDs for the ES-VRF RB tree maintained per-VRF */
+static int bgp_es_vrf_rb_cmp(const struct bgp_evpn_es_vrf *es_vrf1,
+                            const struct bgp_evpn_es_vrf *es_vrf2)
+{
+       return memcmp(&es_vrf1->es->esi, &es_vrf2->es->esi, ESI_BYTES);
+}
+RB_GENERATE(bgp_es_vrf_rb_head, bgp_evpn_es_vrf, rb_node, bgp_es_vrf_rb_cmp);
+
+/* Initialize the ES tables maintained per-tenant vrf */
+void bgp_evpn_vrf_es_init(struct bgp *bgp_vrf)
+{
+       /* Initialize the ES-VRF RB tree */
+       RB_INIT(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree);
+}
+
+/* find the ES-VRF in the per-VRF RB tree */
+static struct bgp_evpn_es_vrf *bgp_evpn_es_vrf_find(struct bgp_evpn_es *es,
+                                                   struct bgp *bgp_vrf)
+{
+       struct bgp_evpn_es_vrf es_vrf;
+
+       es_vrf.es = es;
+
+       return RB_FIND(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree, &es_vrf);
+}
+
+/* allocate a new ES-VRF and setup L3NHG for it */
+static struct bgp_evpn_es_vrf *bgp_evpn_es_vrf_create(struct bgp_evpn_es *es,
+                                                     struct bgp *bgp_vrf)
+{
+       struct bgp_evpn_es_vrf *es_vrf;
+
+       es_vrf = XCALLOC(MTYPE_BGP_EVPN_ES_VRF, sizeof(*es_vrf));
+
+       es_vrf->es = es;
+       es_vrf->bgp_vrf = bgp_vrf;
+
+       /* insert into the VRF-ESI rb tree */
+       if (RB_INSERT(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree, es_vrf)) {
+               XFREE(MTYPE_BGP_EVPN_ES_VRF, es_vrf);
+               return NULL;
+       }
+
+       /* add to the ES's VRF list */
+       listnode_init(&es_vrf->es_listnode, es_vrf);
+       listnode_add(es->es_vrf_list, &es_vrf->es_listnode);
+
+       /* setup the L3 NHG id for the ES */
+       es_vrf->nhg_id = bgp_l3nhg_id_alloc();
+       if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+               zlog_debug("es %s vrf %u nhg 0x%x create", es->esi_str,
+                          bgp_vrf->vrf_id, es_vrf->nhg_id);
+       bgp_evpn_l3nhg_activate(es_vrf, false /* update */);
+
+       return es_vrf;
+}
+
+/* remove the L3-NHG associated with the ES-VRF and free it */
+static void bgp_evpn_es_vrf_delete(struct bgp_evpn_es_vrf *es_vrf)
+{
+       struct bgp_evpn_es *es = es_vrf->es;
+       struct bgp *bgp_vrf = es_vrf->bgp_vrf;
+
+       if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+               zlog_debug("es %s vrf %u nhg 0x%x delete", es->esi_str,
+                          bgp_vrf->vrf_id, es_vrf->nhg_id);
+
+       /* Remove the NHG resources */
+       bgp_evpn_l3nhg_deactivate(es_vrf);
+       if (es_vrf->nhg_id)
+               bgp_l3nhg_id_free(es_vrf->nhg_id);
+       es_vrf->nhg_id = 0;
+
+       /* remove from the ES's VRF list */
+       list_delete_node(es->es_vrf_list, &es_vrf->es_listnode);
+
+       /* remove from the VRF-ESI rb tree */
+       RB_REMOVE(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree, es_vrf);
+
+       XFREE(MTYPE_BGP_EVPN_ES_VRF, es_vrf);
+}
+
+/* deref and delete if there are no references */
+void bgp_evpn_es_vrf_deref(struct bgp_evpn_es_evi *es_evi)
+{
+       struct bgp_evpn_es_vrf *es_vrf = es_evi->es_vrf;
+
+       if (!es_vrf)
+               return;
+
+       if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+               zlog_debug("es-evi %s vni %u vrf %u de-ref",
+                          es_evi->es->esi_str, es_evi->vpn->vni,
+                          es_vrf->bgp_vrf->vrf_id);
+
+       es_evi->es_vrf = NULL;
+       if (es_vrf->ref_cnt)
+               --es_vrf->ref_cnt;
+
+       if (!es_vrf->ref_cnt)
+               bgp_evpn_es_vrf_delete(es_vrf);
+}
+
+/* find or create and reference */
+void bgp_evpn_es_vrf_ref(struct bgp_evpn_es_evi *es_evi, struct bgp *bgp_vrf)
+{
+       struct bgp_evpn_es *es = es_evi->es;
+       struct bgp_evpn_es_vrf *es_vrf = es_evi->es_vrf;
+       struct bgp *old_bgp_vrf = NULL;
+
+       if (es_vrf)
+               old_bgp_vrf = es_vrf->bgp_vrf;
+
+       if (old_bgp_vrf == bgp_vrf)
+               return;
+
+       /* deref the old ES-VRF */
+       bgp_evpn_es_vrf_deref(es_evi);
+
+       if (!bgp_vrf)
+               return;
+
+       if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+               zlog_debug("es-evi %s vni %u vrf %u ref", es_evi->es->esi_str,
+                          es_evi->vpn->vni, bgp_vrf->vrf_id);
+
+       /* find-create the new ES-VRF */
+       es_vrf = bgp_evpn_es_vrf_find(es, bgp_vrf);
+       if (!es_vrf)
+               es_vrf = bgp_evpn_es_vrf_create(es, bgp_vrf);
+       if (!es_vrf)
+               return;
+
+       es_evi->es_vrf = es_vrf;
+       ++es_vrf->ref_cnt;
+}
+
+/* When the L2-VNI is associated with a L3-VNI/VRF update all the
+ * associated ES-EVI entries
+ */
+void bgp_evpn_es_evi_vrf_deref(struct bgpevpn *vpn)
+{
+       struct bgp_evpn_es_evi *es_evi;
+
+       if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+               zlog_debug("es-vrf de-ref for vni %u", vpn->vni);
+
+       RB_FOREACH (es_evi, bgp_es_evi_rb_head, &vpn->es_evi_rb_tree)
+               bgp_evpn_es_vrf_deref(es_evi);
+}
+void bgp_evpn_es_evi_vrf_ref(struct bgpevpn *vpn)
+{
+       struct bgp_evpn_es_evi *es_evi;
+
+       if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+               zlog_debug("es-vrf ref for vni %u", vpn->vni);
+
+       RB_FOREACH (es_evi, bgp_es_evi_rb_head, &vpn->es_evi_rb_tree)
+               bgp_evpn_es_vrf_ref(es_evi, vpn->bgp_vrf);
+}
+
 /*****************************************************************************/
 /* Ethernet Segment to EVI association -
  * 1. The ES-EVI entry is maintained as a RB tree per L2-VNI
@@ -2152,6 +2426,8 @@ static struct bgp_evpn_es_evi *bgp_evpn_es_evi_new(struct bgp_evpn_es *es,
        listnode_init(&es_evi->es_listnode, es_evi);
        listnode_add(es->es_evi_list, &es_evi->es_listnode);
 
+       bgp_evpn_es_vrf_ref(es_evi, vpn->bgp_vrf);
+
        return es_evi;
 }
 
@@ -2169,6 +2445,8 @@ static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi)
        if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL | BGP_EVPNES_EVI_REMOTE))
                return;
 
+       bgp_evpn_es_vrf_deref(es_evi);
+
        /* remove from the ES's VNI list */
        list_delete_node(es->es_evi_list, &es_evi->es_listnode);
 
index d719524bddbc31e88fc8f85a954f24a7ae25ce3f..bf957e581abd4d1cb11b54b079e8b49fba92ecd1 100644 (file)
@@ -96,6 +96,9 @@ struct bgp_evpn_es {
        /* List of ES-EVIs associated with this ES */
        struct list *es_evi_list;
 
+       /* List of ES-VRFs associated with this ES */
+       struct list *es_vrf_list;
+
        /* Number of remote VNIs referencing this ES */
        uint32_t remote_es_evi_cnt;
 
@@ -142,6 +145,33 @@ struct bgp_evpn_es_vtep {
        struct listnode es_listnode;
 };
 
+/* ES-VRF element needed for managing L3 NHGs. It is implicitly created
+ * when an ES-EVI is associated with a tenant VRF
+ */
+struct bgp_evpn_es_vrf {
+       struct bgp_evpn_es *es;
+       struct bgp *bgp_vrf;
+
+       uint32_t flags;
+/* NHG can only be activated if there are active VTEPs in the ES and
+ * there is a valid L3-VNI associated with the VRF
+ */
+#define BGP_EVPNES_VRF_NHG_ACTIVE (1 << 0)
+
+       /* memory used for adding the es_vrf to
+        * es_vrf->bgp_vrf->es_vrf_rb_tree
+        */
+       RB_ENTRY(bgp_evpn_es_vrf) rb_node;
+
+       /* memory used for linking the es_vrf to es_vrf->es->es_vrf_list */
+       struct listnode es_listnode;
+
+       uint32_t nhg_id;
+
+       /* Number of ES-EVI entries associated with this ES-VRF */
+       uint32_t ref_cnt;
+};
+
 /* ES per-EVI info
  * - ES-EVIs are maintained per-L2-VNI (vpn->es_evi_rb_tree)
  * - ES-EVIs are also linked to the parent ES (es->es_evi_list)
@@ -175,6 +205,8 @@ struct bgp_evpn_es_evi {
 
        /* list of PEs (bgp_evpn_es_evi_vtep) attached to the ES for this VNI */
        struct list *es_evi_vtep_list;
+
+       struct bgp_evpn_es_vrf *es_vrf;
 };
 
 /* PE attached to an ES for a VNI. This entry is created when an EAD-per-ES
@@ -308,5 +340,9 @@ void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni,
 void bgp_evpn_es_evi_show(struct vty *vty, bool uj, bool detail);
 struct bgp_evpn_es *bgp_evpn_es_find(const esi_t *esi);
 extern bool bgp_evpn_is_esi_local(esi_t *esi);
+extern void bgp_evpn_vrf_es_init(struct bgp *bgp_vrf);
+extern void bgp_evpn_es_vrf_deref(struct bgp_evpn_es_evi *es_evi);
+extern void bgp_evpn_es_vrf_ref(struct bgp_evpn_es_evi *es_evi,
+                               struct bgp *bgp_vrf);
 
 #endif /* _FRR_BGP_EVPN_MH_H */
index c47576c00c5a768943f1583a73a17241f936b1e1..e8e68c8387223d16ed4414f2e5cd64c01786eaa3 100644 (file)
@@ -218,6 +218,9 @@ static inline struct list *bgpevpn_get_vrf_import_rtl(struct bgpevpn *vpn)
        return vpn->bgp_vrf->vrf_import_rtl;
 }
 
+extern void bgp_evpn_es_evi_vrf_ref(struct bgpevpn *vpn);
+extern void bgp_evpn_es_evi_vrf_deref(struct bgpevpn *vpn);
+
 static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn)
 {
        /* bail if vpn is not associated to bgp_vrf */
@@ -227,6 +230,8 @@ static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn)
        UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
        listnode_delete(vpn->bgp_vrf->l2vnis, vpn);
 
+       bgp_evpn_es_evi_vrf_deref(vpn);
+
        /* remove the backpointer to the vrf instance */
        bgp_unlock(vpn->bgp_vrf);
        vpn->bgp_vrf = NULL;
@@ -255,6 +260,8 @@ static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
        if (bgp_vrf->l3vni &&
            !CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY))
                SET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
+
+       bgp_evpn_es_evi_vrf_ref(vpn);
 }
 
 static inline int is_vni_configured(struct bgpevpn *vpn)
index 21c880e95b2b79133f1130f588b87981373b9844..f961647778b063bcfb128f6a937efccf60eb237f 100644 (file)
@@ -63,6 +63,7 @@
 #include "lib/routing_nb.h"
 #include "bgpd/bgp_nb.h"
 #include "bgpd/bgp_evpn_mh.h"
+#include "bgpd/bgp_nht.h"
 
 #ifdef ENABLE_BGP_VNC
 #include "bgpd/rfapi/rfapi_backend.h"
@@ -209,6 +210,7 @@ static __attribute__((__noreturn__)) void bgp_exit(int status)
                bgp_delete(bgp_default);
 
        bgp_evpn_mh_finish();
+       bgp_l3nhg_finish();
 
        /* reverse bgp_dump_init */
        bgp_dump_finish();
index d8fc98e0484209b68763f2b3abcac2b95e4d4c2a..1582b90a2e7f36c6abc6916faf905752a3bffc94 100644 (file)
@@ -121,6 +121,7 @@ DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP")
 DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI_VTEP, "BGP EVPN ES-EVI VTEP")
 DEFINE_MTYPE(BGPD, BGP_EVPN_ES, "BGP EVPN ESI Information")
 DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI, "BGP EVPN ES-per-EVI Information")
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VRF, "BGP EVPN ES-per-VRF Information")
 DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT")
 DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT")
 DEFINE_MTYPE(BGPD, BGP_EVPN_MACIP, "BGP EVPN MAC IP")
index d1ae392c65ad5605f642d3b2ec7a87f961fc9918..058fa4b29599e9803b6e9e34c3047cc6e6ecff6a 100644 (file)
@@ -114,6 +114,7 @@ DECLARE_MTYPE(LCOMMUNITY_VAL)
 DECLARE_MTYPE(BGP_EVPN_MH_INFO)
 DECLARE_MTYPE(BGP_EVPN_ES)
 DECLARE_MTYPE(BGP_EVPN_ES_EVI)
+DECLARE_MTYPE(BGP_EVPN_ES_VRF)
 DECLARE_MTYPE(BGP_EVPN_ES_VTEP)
 DECLARE_MTYPE(BGP_EVPN_ES_EVI_VTEP)
 
index 9635fe12247a8005d6dc1998375a727aedf4640b..f4acf379fc155c7f54a5d506eff03eb5d8b5b2a5 100644 (file)
@@ -966,3 +966,41 @@ void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer)
                                                0);
        }
 }
+
+/****************************************************************************
+ * L3 NHGs are used for fast failover of nexthops in the dplane. These are
+ * the APIs for allocating L3 NHG ids. Management of the L3 NHG itself is
+ * left to the application using it.
+ * PS: Currently EVPN host routes is the only app using L3 NHG for fast
+ * failover of remote ES links.
+ ***************************************************************************/
+static bitfield_t bgp_nh_id_bitmap;
+
+uint32_t bgp_l3nhg_id_alloc(void)
+{
+       uint32_t nhg_id = 0;
+
+       bf_assign_index(bgp_nh_id_bitmap, nhg_id);
+
+       return nhg_id;
+}
+
+void bgp_l3nhg_id_free(uint32_t nhg_id)
+{
+       if (!nhg_id)
+               return;
+
+       bf_release_index(bgp_nh_id_bitmap, nhg_id);
+}
+
+void bgp_l3nhg_init(void)
+{
+#define BGP_NH_ID_MAX (16 * 1024)
+       bf_init(bgp_nh_id_bitmap, BGP_NH_ID_MAX);
+       bf_assign_zero_index(bgp_nh_id_bitmap);
+}
+
+void bgp_l3nhg_finish(void)
+{
+       bf_free(bgp_nh_id_bitmap);
+}
index 4e015e4aaeb6aeee386b6aac3347258d191eb0eb..8451f0689d87aef79fa522c5980deac5e3f7da72 100644 (file)
@@ -90,4 +90,10 @@ extern void bgp_nht_register_nexthops(struct bgp *bgp);
 extern void bgp_nht_reg_enhe_cap_intfs(struct peer *peer);
 extern void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer);
 
+/* APIs for setting up and allocating L3 nexthop group ids */
+extern uint32_t bgp_l3nhg_id_alloc(void);
+extern void bgp_l3nhg_id_free(uint32_t nhg_id);
+extern void bgp_l3nhg_init(void);
+void bgp_l3nhg_finish(void);
+
 #endif /* _BGP_NHT_H */
index 25f3526a23deca96202d5b4f6a05295df23fbd91..82ce0c3882c5b7fad552093d01d77c8614257f32 100644 (file)
@@ -3156,6 +3156,7 @@ static struct bgp *bgp_create(as_t *as, const char *name,
                                 sizeof(struct bgp_evpn_info));
 
        bgp_evpn_init(bgp);
+       bgp_evpn_vrf_es_init(bgp);
        bgp_pbr_init(bgp);
 
        /*initilize global GR FSM */
@@ -7327,6 +7328,7 @@ void bgp_master_init(struct thread_master *master, const int buffer_size)
        /* mpls label dynamic allocation pool */
        bgp_lp_init(bm->master, &bm->labelpool);
 
+       bgp_l3nhg_init();
        bgp_evpn_mh_init();
        QOBJ_REG(bm, bgp_master);
 }
index d22fd008d8f561f1b8a24b5474965532024f169a..965a35b345d95f6bea58c6bb69a3d042a7f95cfb 100644 (file)
@@ -302,6 +302,9 @@ enum bgp_link_bw_handling {
        BGP_LINK_BW_DEFWT_4_MISSING
 };
 
+RB_HEAD(bgp_es_vrf_rb_head, bgp_evpn_es_vrf);
+RB_PROTOTYPE(bgp_es_vrf_rb_head, bgp_evpn_es_vrf, rb_node, bgp_es_vrf_rb_cmp);
+
 /* BGP instance structure.  */
 struct bgp {
        /* AS number of this BGP instance.  */
@@ -637,6 +640,9 @@ struct bgp {
        /* SVI associated with the L3-VNI corresponding to this vrf */
        ifindex_t l3vni_svi_ifindex;
 
+       /* RB tree of ES-VRFs */
+       struct bgp_es_vrf_rb_head es_vrf_rb_tree;
+
        /* vrf flags */
        uint32_t vrf_flags;
 #define BGP_VRF_AUTO                        (1 << 0)