return 0;
}
+#define BGP_PROC_L2VNI_LIMIT 10
+static int install_evpn_remote_route_per_l2vni(struct bgp *bgp, struct bgp_path_info *pi,
+ const struct prefix_evpn *evp)
+{
+ int ret = 0;
+ uint8_t vni_iter = 0;
+ struct bgpevpn *t_vpn = NULL;
+ struct bgpevpn *t_vpn_next = NULL;
+
+ for (t_vpn = zebra_l2_vni_first(&bm->zebra_l2_vni_head);
+ t_vpn && vni_iter < BGP_PROC_L2VNI_LIMIT; t_vpn = t_vpn_next) {
+ t_vpn_next = zebra_l2_vni_next(&bm->zebra_l2_vni_head, t_vpn);
+ vni_iter++;
+ /*
+ * Skip install/uninstall if the route entry is not needed to
+ * be imported into the VNI i.e. RTs dont match
+ */
+ if (!is_route_matching_for_vni(bgp, t_vpn, pi))
+ continue;
+
+ ret = install_evpn_route_entry(bgp, t_vpn, evp, pi);
+
+ if (ret) {
+ flog_err(EC_BGP_EVPN_FAIL,
+ "%u: Failed to install EVPN %s route in VNI %u during BP",
+ bgp->vrf_id, bgp_evpn_route_type_str[evp->prefix.route_type].str,
+ t_vpn->vni);
+ zebra_l2_vni_del(&bm->zebra_l2_vni_head, t_vpn);
+
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
/*
* Install or uninstall routes of specified type that are appropriate for this
* particular VNI.
*/
-static int install_uninstall_routes_for_vni(struct bgp *bgp,
- struct bgpevpn *vpn, bool install)
+int install_uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn, bool install)
{
afi_t afi;
safi_t safi;
struct bgp_dest *rd_dest, *dest;
struct bgp_table *table;
struct bgp_path_info *pi;
- int ret;
+ int ret = 0;
+ uint8_t count = 0;
+ bool walk_fifo = false;
afi = AFI_L2VPN;
safi = SAFI_EVPN;
- /* Walk entire global routing table and evaluate routes which could be
+ if (!bgp) {
+ walk_fifo = true;
+ bgp = bgp_get_evpn();
+ if (!bgp) {
+ zlog_warn("%s: No BGP EVPN instance found...", __func__);
+
+ return -1;
+ }
+ }
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Total %u L2VNI VPNs pending to be processed for remote route installation",
+ __func__, (uint32_t)zebra_l2_vni_count(&bm->zebra_l2_vni_head));
+ /*
+ * Walk entire global routing table and evaluate routes which could be
* imported into this VPN. Note that we cannot just look at the routes
- * for
- * the VNI's RD - remote routes applicable for this VNI could have any
- * RD.
+ * for the VNI's RD - remote routes applicable for this VNI could have
+ * any RD.
+ * Note: EVPN routes are a 2-level table.
*/
- /* EVPN routes are a 2-level table. */
for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest;
rd_dest = bgp_route_next(rd_dest)) {
table = bgp_dest_get_bgp_table_info(rd_dest);
(const struct prefix_evpn *)bgp_dest_get_prefix(
dest);
- if (evp->prefix.route_type != BGP_EVPN_IMET_ROUTE &&
- evp->prefix.route_type != BGP_EVPN_AD_ROUTE &&
- evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ /* Proceed only for AD, MAC_IP and IMET routes */
+ switch (evp->prefix.route_type) {
+ case BGP_EVPN_AD_ROUTE:
+ case BGP_EVPN_MAC_IP_ROUTE:
+ case BGP_EVPN_IMET_ROUTE:
+ break;
+ case BGP_EVPN_ES_ROUTE:
+ case BGP_EVPN_IP_PREFIX_ROUTE:
continue;
+ }
for (pi = bgp_dest_get_bgp_path_info(dest); pi;
pi = pi->next) {
- /* Consider "valid" remote routes applicable for
- * this VNI. */
- if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID)
- && pi->type == ZEBRA_ROUTE_BGP
- && pi->sub_type == BGP_ROUTE_NORMAL))
- continue;
-
- if (!is_route_matching_for_vni(bgp, vpn, pi))
+ /*
+ * Skip install/uninstall if
+ * - Not a valid remote routes
+ * - Install & evpn route matchesi macvrf SOO
+ */
+ if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID) &&
+ pi->type == ZEBRA_ROUTE_BGP &&
+ pi->sub_type == BGP_ROUTE_NORMAL) ||
+ (install && bgp_evpn_route_matches_macvrf_soo(pi, evp)))
continue;
- if (install) {
- if (bgp_evpn_route_matches_macvrf_soo(
- pi, evp))
+ if (walk_fifo) {
+ ret = install_evpn_remote_route_per_l2vni(bgp, pi, evp);
+ if (ret) {
+ bgp_dest_unlock_node(rd_dest);
+ bgp_dest_unlock_node(dest);
+ return ret;
+ }
+ } else {
+ /*
+ * Skip install/uninstall if the route
+ * entry is not needed to be imported
+ * into the VNI i.e. RTs dont match
+ */
+ if (!is_route_matching_for_vni(bgp, vpn, pi))
continue;
- ret = install_evpn_route_entry(bgp, vpn,
- evp, pi);
- } else
- ret = uninstall_evpn_route_entry(
- bgp, vpn, evp, pi);
-
- if (ret) {
- flog_err(EC_BGP_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);
-
- bgp_dest_unlock_node(rd_dest);
- bgp_dest_unlock_node(dest);
- return ret;
+ if (install)
+ ret = install_evpn_route_entry(bgp, vpn, evp, pi);
+ else
+ ret = uninstall_evpn_route_entry(bgp, vpn, evp, pi);
+
+ if (ret) {
+ flog_err(EC_BGP_EVPN_FAIL,
+ "%u: Failed to %s EVPN %s route in VNI %u",
+ bgp->vrf_id,
+ install ? "install" : "uninstall",
+ bgp_evpn_route_type_str[evp->prefix.route_type]
+ .str,
+ vpn->vni);
+
+ bgp_dest_unlock_node(rd_dest);
+ bgp_dest_unlock_node(dest);
+ return ret;
+ }
}
}
}
}
+ if (walk_fifo) {
+ while (count < BGP_PROC_L2VNI_LIMIT) {
+ vpn = zebra_l2_vni_pop(&bm->zebra_l2_vni_head);
+ if (!vpn)
+ return 0;
+
+ UNSET_FLAG(vpn->flags, VNI_FLAG_ADD);
+ count++;
+ }
+ }
+
return 0;
}
return 0;
}
+static void bgp_evpn_l2vni_remote_route_processing(struct bgpevpn *vpn)
+{
+ /*
+ * Anytime BGP gets a Bulk of L2 VNIs ADD/UPD from zebra,
+ * - Walking the entire global routing table per VNI is very expensive.
+ * - The next read (say of another VNI ADD/UPD) from the socket does
+ * not proceed unless this walk is complete.
+ * This results in huge output buffer FIFO growth spiking up the
+ * memory in zebra.
+ *
+ * To avoid this, idea is to hookup the VPN off the struct bgp_master
+ * and maintain a VPN FIFO list which is processed later on, where we
+ * walk a chunk of VPNs and do the remote route install.
+ */
+ if (!CHECK_FLAG(vpn->flags, VNI_FLAG_ADD)) {
+ zebra_l2_vni_add_tail(&bm->zebra_l2_vni_head, vpn);
+ SET_FLAG(vpn->flags, VNI_FLAG_ADD);
+ }
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Scheduling L2VNI ADD to be processed later for VNI %u", vpn->vni);
+
+ /*
+ * If there are no VNI's in the bgp VPN FIFO list i.e. an update
+ * for an already processed VNI comes in, schedule the remote
+ * route install immediately.
+ *
+ * In all other cases, it is ok to schedule the remote route install
+ * after a small sleep. This is to give benefit of doubt in case more
+ * L2VNI ADD events come.
+ */
+ if (zebra_l2_vni_count(&bm->zebra_l2_vni_head))
+ event_add_timer_msec(bm->master, bgp_zebra_process_remote_routes_for_l2vni, NULL,
+ 20, &bm->t_bgp_zebra_l2_vni);
+ else
+ event_add_event(bm->master, bgp_zebra_process_remote_routes_for_l2vni, NULL, 0,
+ &bm->t_bgp_zebra_l2_vni);
+}
+
/*
* When bgp instance goes down also clean up what might have been left over
* from evpn.
if (!vpn)
return 0;
+ /* Remove the VPN from the bgp VPN FIFO (if exists) */
+ UNSET_FLAG(vpn->flags, VNI_FLAG_ADD);
+ zebra_l2_vni_del(&bm->zebra_l2_vni_head, vpn);
+
/* Remove all local EVPN routes and schedule for processing (to
* withdraw from peers).
*/
}
}
- /* If we have learnt and retained remote routes (VTEPs, MACs) for this
- * VNI,
- * install them.
- */
- install_routes_for_vni(bgp, vpn);
-
/* If we are advertising gateway mac-ip
It needs to be conveyed again to zebra */
bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip, vpn->vni);
/* advertise svi mac-ip knob to zebra */
bgp_zebra_advertise_svi_macip(bgp, vpn->advertise_svi_macip, vpn->vni);
+ bgp_evpn_l2vni_remote_route_processing(vpn);
+
return 0;
}
*/
void bgp_evpn_cleanup_on_disable(struct bgp *bgp)
{
- hash_iterate(bgp->vnihash, (void (*)(struct hash_bucket *,
- void *))cleanup_vni_on_disable,
+ struct bgpevpn *vpn = NULL;
+ uint32_t vni_count = zebra_l2_vni_count(&bm->zebra_l2_vni_head);
+
+ /* Cleanup VNI FIFO list from this bgp instance */
+ while (vni_count) {
+ vpn = zebra_l2_vni_pop(&bm->zebra_l2_vni_head);
+ UNSET_FLAG(vpn->flags, VNI_FLAG_ADD);
+ vni_count--;
+ }
+
+ hash_iterate(bgp->vnihash, (void (*)(struct hash_bucket *, void *))cleanup_vni_on_disable,
bgp);
}
afi_t afi;
safi_t safi;
int i;
+ uint32_t vni_count;
+ struct bgpevpn *vpn = NULL;
struct bgp_dest *dest = NULL;
struct bgp_dest *dest_next = NULL;
struct bgp_table *dest_table = NULL;
struct graceful_restart_info *gr_info;
- uint32_t cnt_before, cnt_after;
+ uint32_t b_ann_cnt = 0, b_l2_cnt = 0;
+ uint32_t a_ann_cnt = 0, a_l2_cnt = 0;
assert(bgp);
* Iterate the pending dest list and remove all the dest pertaininig to
* the bgp under delete.
*/
- cnt_before = zebra_announce_count(&bm->zebra_announce_head);
+ b_ann_cnt = zebra_announce_count(&bm->zebra_announce_head);
for (dest = zebra_announce_first(&bm->zebra_announce_head); dest;
dest = dest_next) {
dest_next = zebra_announce_next(&bm->zebra_announce_head, dest);
}
}
- cnt_after = zebra_announce_count(&bm->zebra_announce_head);
- if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("Zebra Announce Fifo cleanup count before %u and after %u during BGP %s deletion",
- cnt_before, cnt_after, bgp->name_pretty);
+ /*
+ * Pop all VPNs yet to be processed for remote routes install if the
+ * bgp-evpn instance is getting deleted
+ */
+ if (bgp == bgp_get_evpn()) {
+ b_l2_cnt = zebra_l2_vni_count(&bm->zebra_l2_vni_head);
+ vni_count = b_l2_cnt;
+ while (vni_count) {
+ vpn = zebra_l2_vni_pop(&bm->zebra_l2_vni_head);
+ UNSET_FLAG(vpn->flags, VNI_FLAG_ADD);
+ vni_count--;
+ }
+ }
+
+ if (BGP_DEBUG(zebra, ZEBRA)) {
+ a_ann_cnt = zebra_announce_count(&bm->zebra_announce_head);
+ a_l2_cnt = zebra_l2_vni_count(&bm->zebra_l2_vni_head);
+ zlog_debug("FIFO Cleanup Count during BGP %s deletion :: "
+ "Zebra Announce - before %u after %u :: "
+ "BGP L2_VNI - before %u after %u",
+ bgp->name_pretty, b_ann_cnt, a_ann_cnt, b_l2_cnt, a_l2_cnt);
+ }
bgp_soft_reconfig_table_task_cancel(bgp, NULL, NULL);
bm = &bgp_master;
zebra_announce_init(&bm->zebra_announce_head);
+ zebra_l2_vni_init(&bm->zebra_l2_vni_head);
bm->bgp = list_new();
bm->listen_sockets = list_new();
bm->port = BGP_PORT_DEFAULT;
bm->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
bm->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME;
bm->rib_stale_time = BGP_DEFAULT_RIB_STALE_TIME;
+ bm->t_bgp_zebra_l2_vni = NULL;
bgp_mac_init();
/* init the rd id space.
EVENT_OFF(bm->t_bgp_sync_label_manager);
EVENT_OFF(bm->t_bgp_start_label_manager);
EVENT_OFF(bm->t_bgp_zebra_route);
+ EVENT_OFF(bm->t_bgp_zebra_l2_vni);
bgp_mac_finish();
}