* or the global route table.
*/
static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
- afi_t afi, safi_t safi, struct bgp_dest *dest,
- struct attr *attr, int add,
- struct bgp_path_info **pi, uint8_t flags,
- uint32_t seq, bool setup_sync,
+ afi_t afi, safi_t safi,
+ struct bgp_dest *dest, struct attr *attr,
+ int add, struct bgp_path_info **pi,
+ uint8_t flags, uint32_t seq, bool vpn_rt,
bool *old_is_sync)
{
struct bgp_path_info *tmp_pi;
/* if a local path is being added with a non-zero esi look
* for SYNC paths from ES peers and bubble up the sync-info
*/
- update_evpn_route_entry_sync_info(bgp, dest, attr, seq, setup_sync);
+ update_evpn_route_entry_sync_info(bgp, dest, attr, seq, vpn_rt);
/* For non-GW MACs, update MAC mobility seq number, if needed. */
if (seq && !CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW))
}
}
+ /* MAC-IP routes in the VNI route table are linked to the
+ * destination ES
+ */
+ if (route_change && vpn_rt
+ && (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE))
+ bgp_evpn_path_es_link(tmp_pi, vpn->vni,
+ bgp_evpn_attr_get_esi(tmp_pi->attr));
+
/* Return back the route entry. */
*pi = tmp_pi;
return route_change;
if (!pi) {
/* Create an info */
- (void)bgp_create_evpn_bgp_path_info(parent_pi, dest,
+ pi = bgp_create_evpn_bgp_path_info(parent_pi, dest,
parent_pi->attr);
} else {
if (attrhash_cmp(pi->attr, parent_pi->attr)
pi->uptime = bgp_clock();
}
+ /* MAC-IP routes in the VNI table are linked to the destination ES */
+ if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ bgp_evpn_path_es_link(pi, vpn->vni,
+ bgp_evpn_attr_get_esi(pi->attr));
+
/* Perform route selection and update zebra, if required. */
ret = evpn_route_select_install(bgp, vpn, dest);
bgp_evpn_skip_vrf_import_of_local_es(const struct prefix_evpn *evp,
struct bgp_path_info *pi, int install)
{
- if ((evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
- && bgp_evpn_attr_is_local_es(pi->attr)) {
- if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
- char esi_buf[ESI_STR_LEN];
- char prefix_buf[PREFIX_STRLEN];
+ esi_t *esi;
+ struct in_addr nh;
- zlog_debug(
- "vrf %s of evpn prefix %s skipped, local es %s",
- install ? "import" : "unimport",
- prefix2str(evp, prefix_buf, sizeof(prefix_buf)),
- esi_to_str(bgp_evpn_attr_get_esi(pi->attr),
- esi_buf, sizeof(esi_buf)));
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
+ esi = bgp_evpn_attr_get_esi(pi->attr);
+
+ /* Don't import routes that point to a local destination */
+ if (bgp_evpn_attr_is_local_es(pi->attr)) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
+ char esi_buf[ESI_STR_LEN];
+
+ zlog_debug(
+ "vrf %s of evpn prefix %pFX skipped, local es %s",
+ install ? "import" : "unimport", evp,
+ esi_to_str(esi, esi_buf,
+ sizeof(esi_buf)));
+ }
+ return true;
+ }
+
+ /* Don't import routes with ES as destination if the nexthop
+ * has not been advertised via the EAD-ES
+ */
+ if (pi->attr)
+ nh = pi->attr->nexthop;
+ else
+ nh.s_addr = 0;
+ if (!bgp_evpn_es_is_vtep_active(esi, nh)) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
+ char esi_buf[ESI_STR_LEN];
+
+ zlog_debug(
+ "vrf %s of evpn prefix %pFX skipped, nh %pI4 inactive in es %s",
+ install ? "import" : "unimport", evp,
+ &nh,
+ esi_to_str(esi, esi_buf,
+ sizeof(esi_buf)));
+ }
+ return true;
}
- return true;
}
return false;
}
/*
* Install or uninstall route for appropriate VNIs/ESIs.
*/
-static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
- const struct prefix *p,
- struct bgp_path_info *pi, int import)
+static int bgp_evpn_install_uninstall_table(struct bgp *bgp, afi_t afi,
+ safi_t safi, const struct prefix *p,
+ struct bgp_path_info *pi,
+ int import, bool in_vni_rt,
+ bool in_vrf_rt)
{
struct prefix_evpn *evp = (struct prefix_evpn *)p;
struct attr *attr = pi->attr;
evp->prefix.route_type == BGP_EVPN_AD_ROUTE ||
evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
- irt = lookup_import_rt(bgp, eval);
+ irt = in_vni_rt ? lookup_import_rt(bgp, eval) : NULL;
if (irt)
install_uninstall_route_in_vnis(
bgp, afi, safi, evp, pi, irt->vnis,
import);
- vrf_irt = lookup_vrf_import_rt(eval);
+ vrf_irt = in_vrf_rt ? lookup_vrf_import_rt(eval) : NULL;
if (vrf_irt)
install_uninstall_route_in_vrfs(
bgp, afi, safi, evp, pi, vrf_irt->vrfs,
|| type == ECOMMUNITY_ENCODE_IP) {
memcpy(&eval_tmp, eval, ecom->unit_size);
mask_ecom_global_admin(&eval_tmp, eval);
- irt = lookup_import_rt(bgp, &eval_tmp);
- vrf_irt = lookup_vrf_import_rt(&eval_tmp);
+ if (in_vni_rt)
+ irt = lookup_import_rt(bgp, &eval_tmp);
+ if (in_vrf_rt)
+ vrf_irt =
+ lookup_vrf_import_rt(&eval_tmp);
}
if (irt)
return 0;
}
+/*
+ * Install or uninstall route for appropriate VNIs/ESIs.
+ */
+static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
+ const struct prefix *p,
+ struct bgp_path_info *pi, int import)
+{
+ return bgp_evpn_install_uninstall_table(bgp, afi, safi, p, pi, import,
+ true, true);
+}
+
+/* Import the pi into vrf routing tables */
+void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import)
+{
+ struct bgp *bgp_evpn;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return;
+
+ bgp_evpn_install_uninstall_table(bgp_evpn, AFI_L2VPN, SAFI_EVPN,
+ &pi->net->p, pi, import, false /*vpn*/,
+ true /*vrf*/);
+}
+
/*
* delete and withdraw all ipv4 and ipv6 routes in the vrf table as type-5
* routes
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);
+static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi);
+static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller);
+static void bgp_evpn_es_path_all_update(struct bgp_evpn_es_vtep *es_vtep,
+ bool active);
esi_t zero_esi_buf, *zero_esi = &zero_esi_buf;
/* 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);
+ /* If VTEP becomes active update the NHG first and then
+ * the exploded routes. If VTEP becomes inactive update
+ * routes first. This ordering is done to avoid deleting
+ * the NHG while there are dependent routes against
+ * it.
+ */
+ if (new_active) {
+ bgp_evpn_l3nhg_update_on_vtep_chg(es_vtep->es);
+ bgp_evpn_es_path_all_update(es_vtep, true /*active*/);
+ } else {
+ bgp_evpn_es_path_all_update(es_vtep, false /*active*/);
+ 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);
bgp_evpn_es_vtep_do_del(bgp, es_vtep, esr);
}
+bool bgp_evpn_es_is_vtep_active(esi_t *esi, struct in_addr nh)
+{
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_vtep *es_vtep;
+ struct listnode *node = NULL;
+ bool rc = false;
+
+ if (!memcmp(esi, zero_esi, sizeof(*esi)) || !nh.s_addr)
+ return true;
+
+ es = bgp_evpn_es_find(esi);
+ if (!es)
+ return false;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+ if (es_vtep->vtep_ip.s_addr == nh.s_addr) {
+ if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE))
+ rc = true;
+ break;
+ }
+ }
+ return rc;
+}
+
+/********************** ES MAC-IP paths *************************************
+ * MAC-IP routes in the VNI routing table are linked to the destination
+ * ES for efficient updates on ES changes (such as VTEP add/del).
+ ****************************************************************************/
+void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info)
+{
+ bgp_evpn_path_es_unlink(es_info);
+ XFREE(MTYPE_BGP_EVPN_PATH_ES_INFO, es_info);
+}
+
+static struct bgp_path_es_info *
+bgp_evpn_path_es_info_new(struct bgp_path_info *pi, vni_t vni)
+{
+ struct bgp_path_info_extra *e;
+
+ e = bgp_path_info_extra_get(pi);
+
+ /* If es_info doesn't exist allocate it */
+ if (!e->es_info) {
+ e->es_info = XCALLOC(MTYPE_BGP_EVPN_PATH_ES_INFO,
+ sizeof(struct bgp_path_es_info));
+ e->es_info->pi = pi;
+ e->es_info->vni = vni;
+ }
+
+ return e->es_info;
+}
+
+void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info)
+{
+ struct bgp_evpn_es *es = es_info->es;
+ struct bgp_path_info *pi;
+ char prefix_buf[PREFIX_STRLEN];
+
+ if (!es)
+ return;
+
+ pi = es_info->pi;
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug(
+ "path %s unlinked from es %s",
+ prefix2str(&pi->net->p, prefix_buf, sizeof(prefix_buf)),
+ es->esi_str);
+
+ list_delete_node(es->macip_path_list, &es_info->es_listnode);
+ es_info->es = NULL;
+
+ /* if there are no other references against the ES it
+ * needs to be freed
+ */
+ bgp_evpn_es_free(es, __func__);
+
+ /* Note we don't free the path es_info on unlink; it will be freed up
+ * along with the path.
+ */
+}
+
+void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi)
+{
+ struct bgp_path_es_info *es_info;
+ struct bgp_evpn_es *es;
+ struct bgp *bgp_evpn = bgp_get_evpn();
+ char prefix_buf[PREFIX_STRLEN];
+
+ es_info = pi->extra ? pi->extra->es_info : NULL;
+ /* if the esi is zero just unlink the path from the old es */
+ if (!esi || !memcmp(esi, zero_esi, sizeof(*esi))) {
+ if (es_info)
+ bgp_evpn_path_es_unlink(es_info);
+ return;
+ }
+
+ if (!bgp_evpn)
+ return;
+
+ /* setup es_info against the path if it doesn't aleady exist */
+ if (!es_info)
+ es_info = bgp_evpn_path_es_info_new(pi, vni);
+
+ /* find-create ES */
+ es = bgp_evpn_es_find(esi);
+ if (!es)
+ bgp_evpn_es_new(bgp_evpn, esi);
+
+ /* dup check */
+ if (es_info->es == es)
+ return;
+
+ /* unlink old ES if any */
+ bgp_evpn_path_es_unlink(es_info);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug(
+ "path %s linked to es %s",
+ prefix2str(&pi->net->p, prefix_buf, sizeof(prefix_buf)),
+ es->esi_str);
+
+ /* link mac-ip path to the new destination ES */
+ es_info->es = es;
+ listnode_init(&es_info->es_listnode, es_info);
+ listnode_add_sort(es->macip_path_list, &es_info->es_listnode);
+}
+
+static void bgp_evpn_es_path_all_update(struct bgp_evpn_es_vtep *es_vtep,
+ bool active)
+{
+ struct listnode *node;
+ struct bgp_path_es_info *es_info;
+ struct bgp_path_info *pi;
+ struct bgp_path_info *parent_pi;
+ struct bgp_evpn_es *es = es_vtep->es;
+ char prefix_buf[PREFIX_STRLEN];
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("update all paths linked to es %s", es->esi_str);
+
+ for (ALL_LIST_ELEMENTS_RO(es->macip_path_list, node, es_info)) {
+ pi = es_info->pi;
+ if (pi->sub_type != BGP_ROUTE_IMPORTED)
+ continue;
+
+ parent_pi = pi->extra ? pi->extra->parent : NULL;
+ if (!parent_pi || !parent_pi->attr)
+ continue;
+
+ if (es_vtep->vtep_ip.s_addr != parent_pi->attr->nexthop.s_addr)
+ continue;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("update path %s linked to es %s",
+ prefix2str(&parent_pi->net->p, prefix_buf,
+ sizeof(prefix_buf)),
+ es->esi_str);
+ bgp_evpn_import_route_in_vrfs(parent_pi, active ? 1 : 0);
+ }
+}
+
/* compare ES-IDs for the global ES RB tree */
static int bgp_es_rb_cmp(const struct bgp_evpn_es *es1,
const struct bgp_evpn_es *es2)
es->es_vrf_list = list_new();
listset_app_node_mem(es->es_vrf_list);
+ /* Initialise the route list used for efficient event handling */
+ es->macip_path_list = list_new();
+ listset_app_node_mem(es->macip_path_list);
+
QOBJ_REG(es, bgp_evpn_es);
return es;
*/
static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller)
{
- if (es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE))
+ if ((es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE))
+ || listcount(es->macip_path_list))
return;
if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
list_delete(&es->es_evi_list);
list_delete(&es->es_vrf_list);
list_delete(&es->es_vtep_list);
+ list_delete(&es->macip_path_list);
bgp_table_unlock(es->route_table);
/* remove the entry from various databases */
/* List of ES-VRFs associated with this ES */
struct list *es_vrf_list;
+ /* List of MAC-IP global routes using this ES as destination -
+ * element is bgp_path_info_extra->es_info
+ */
+ struct list *macip_path_list;
+
/* Number of remote VNIs referencing this ES */
uint32_t remote_es_evi_cnt;
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);
+extern void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info);
+extern void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info);
+extern void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni,
+ esi_t *esi);
+extern bool bgp_evpn_es_is_vtep_active(esi_t *esi, struct in_addr nh);
#endif /* _FRR_BGP_EVPN_MH_H */
bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi,
const struct prefix_evpn *evp,
struct prefix_rd *prd);
+extern struct bgp_node *bgp_global_evpn_node_get(struct bgp_table *table,
+ afi_t afi, safi_t safi,
+ const struct prefix_evpn *evp,
+ struct prefix_rd *prd);
+extern struct bgp_node *
+bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi,
+ const struct prefix_evpn *evp,
+ struct prefix_rd *prd);
+extern void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import);
#endif /* _BGP_EVPN_PRIVATE_H */
DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information")
DEFINE_MTYPE(BGPD, BGP_EVPN_MH_INFO, "BGP EVPN MH Information")
DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP")
+DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_ES_INFO, "BGP EVPN PATH ES Information")
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")
DECLARE_MTYPE(BGP_EVPN_ES_EVI)
DECLARE_MTYPE(BGP_EVPN_ES_VRF)
DECLARE_MTYPE(BGP_EVPN_ES_VTEP)
+DECLARE_MTYPE(BGP_EVPN_PATH_ES_INFO)
DECLARE_MTYPE(BGP_EVPN_ES_EVI_VTEP)
DECLARE_MTYPE(BGP_EVPN)
if (e->aggr_suppressors)
list_delete(&e->aggr_suppressors);
+ if (e->es_info)
+ bgp_evpn_path_es_info_free(e->es_info);
+
if ((*extra)->bgp_fs_iprule)
list_delete(&((*extra)->bgp_fs_iprule));
if ((*extra)->bgp_fs_pbr)
#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE1_SIZE -15
#define BGP_NLRI_PARSE_ERROR -32
+/* MAC-IP/type-2 path_info in the global routing table is linked to the
+ * destination ES
+ */
+struct bgp_path_es_info {
+ /* back pointer to the route */
+ struct bgp_path_info *pi;
+ vni_t vni;
+ /* destination ES */
+ struct bgp_evpn_es *es;
+ /* memory used for linking the path to the destination ES */
+ struct listnode es_listnode;
+};
+
/* Ancillary information to struct bgp_path_info,
* used for uncommonly used data (aggregation, MPLS, etc.)
* and lazily allocated to save memory.
struct list *bgp_fs_pbr;
/* presence of FS pbr iprule based entry */
struct list *bgp_fs_iprule;
+ /* Destination Ethernet Segment links for EVPN MH */
+ struct bgp_path_es_info *es_info;
};
struct bgp_path_info {