return 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))
+ &p->prefix.macip_addr.ip.ipaddr_v6))
&& CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)
&& bgpevpn_get_l3vni(vpn) && bgp_evpn_es_add_l3_ecomm_ok(esi);
}
struct attr *attr_new;
int ret;
struct prefix_evpn ad_evp;
+ bool old_local_es = false;
+ bool new_local_es;
/* EAD prefix in the global table doesn't include the VTEP-IP so
* we need to create a different copy for the VNI
/* Create an info */
pi = bgp_create_evpn_bgp_path_info(parent_pi, dest,
parent_pi->attr);
+ new_local_es = bgp_evpn_attr_is_local_es(pi->attr);
} else {
if (attrhash_cmp(pi->attr, parent_pi->attr)
&& !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop))
SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED);
+ old_local_es = bgp_evpn_attr_is_local_es(pi->attr);
+ new_local_es = bgp_evpn_attr_is_local_es(attr_new);
+ /* If ESI is different or if its type has changed we
+ * need to reinstall the path in zebra
+ */
+ if ((old_local_es != new_local_es)
+ || memcmp(&pi->attr->esi, &attr_new->esi,
+ sizeof(attr_new->esi))) {
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("VNI %d path %pFX chg to %s es",
+ vpn->vni, &pi->net->p,
+ new_local_es ? "local"
+ : "non-local");
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED);
+ }
+
/* Unintern existing, set to new. */
bgp_attr_unintern(&pi->attr);
pi->attr = attr_new;
* from sync-path to remote-path)
*/
local_pi = bgp_evpn_route_get_local_path(bgp, dest);
- if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr))
+ if (local_pi && (old_local_es || new_local_es))
bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi,
- __func__);
-
+ __func__);
bgp_dest_unlock_node(dest);
return ret;
&& pi->sub_type == BGP_ROUTE_NORMAL))
return 0;
- /* don't import hosts that are locally attached */
- if (bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi, install))
- return 0;
-
if (is_route_matching_for_vrf(bgp_vrf, pi)) {
if (bgp_evpn_route_rmac_self_check(bgp_vrf, evp, pi))
return 0;
- if (install)
+ /* don't import hosts that are locally attached */
+ if (install
+ && !bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi,
+ install))
ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi);
else
ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp,
int ret;
/* don't import hosts that are locally attached */
- if (bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi,
- install))
- continue;
-
- if (install)
+ if (install
+ && !bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi,
+ install))
ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi);
else
ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp,
true, true);
}
+void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import)
+{
+ struct bgp *bgp_evpn;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return;
+
+ install_uninstall_evpn_route(bgp_evpn, AFI_L2VPN, SAFI_EVPN,
+ &pi->net->p, pi, import);
+}
+
/* Import the pi into vrf routing tables */
void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import)
{
if (attr) {
STREAM_GET(&attr->esi, pkt, sizeof(esi_t));
- if (bgp_evpn_is_esi_local(&attr->esi))
+ if (bgp_evpn_is_esi_local_and_non_bypass(&attr->esi))
attr->es_flags |= ATTR_ES_IS_LOCAL;
else
attr->es_flags &= ~ATTR_ES_IS_LOCAL;
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_path_es_unlink(struct bgp_path_es_info *es_info);
+static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es,
+ bool is_local);
esi_t zero_esi_buf, *zero_esi = &zero_esi_buf;
static int bgp_evpn_run_consistency_checks(struct thread *t);
else
list_delete_node(es->macip_global_path_list,
&es_info->es_listnode);
+
es_info->es = NULL;
/* if there are no other references against the ES it
struct bgp_path_es_info *es_info;
struct bgp_evpn_es *es;
struct bgp *bgp_evpn;
- struct prefix_evpn *evp;
es_info = (pi->extra && pi->extra->mh_info)
? pi->extra->mh_info->es_info
if (!bgp_evpn)
return;
- /* Only MAC-IP routes need to be linked (MAC-only routes can be
- * skipped) as these lists are maintained for managing
- * host routes in the tenant VRF
- */
- evp = (struct prefix_evpn *)&pi->net->p;
- if (!(is_evpn_prefix_ipaddr_v4(evp) || is_evpn_prefix_ipaddr_v6(evp)))
- 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);
listnode_add(es->macip_global_path_list, &es_info->es_listnode);
}
+static bool bgp_evpn_is_macip_path(struct bgp_path_info *pi)
+{
+ struct prefix_evpn *evp;
+
+ /* Only MAC-IP routes need to be linked (MAC-only routes can be
+ * skipped) as these lists are maintained for managing
+ * host routes in the tenant VRF
+ */
+ evp = (struct prefix_evpn *)&pi->net->p;
+ return is_evpn_prefix_ipaddr_v4(evp) || is_evpn_prefix_ipaddr_v6(evp);
+}
+
/* When a remote ES is added to a VRF, routes using that as
* a destination need to be migrated to a L3NHG or viceversa.
* This is done indirectly by re-attempting an install of the
for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) {
pi = es_info->pi;
+ if (!bgp_evpn_is_macip_path(pi))
+ continue;
+
if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
zlog_debug(
"update path %pFX linked to es %s on vrf chg",
XFREE(MTYPE_BGP_EVPN_ES, es);
}
+static inline bool bgp_evpn_is_es_local_and_non_bypass(struct bgp_evpn_es *es)
+{
+ return (es->flags & BGP_EVPNES_LOCAL)
+ && !(es->flags & BGP_EVPNES_BYPASS);
+}
+
/* init local info associated with the ES */
static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es)
{
char buf[BGP_EVPN_PREFIX_RD_LEN];
+ bool old_is_local;
+ bool is_local;
if (CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL))
return;
+ old_is_local = bgp_evpn_is_es_local_and_non_bypass(es);
SET_FLAG(es->flags, BGP_EVPNES_LOCAL);
+
listnode_init(&es->es_listnode, es);
listnode_add(bgp_mh_info->local_es_list, &es->es_listnode);
es->prd.prefixlen = 64;
snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id, es->rd_id);
(void)str2prefix_rd(buf, &es->prd);
+
+ is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ if (old_is_local != is_local)
+ bgp_evpn_mac_update_on_es_local_chg(es, is_local);
}
/* clear any local info associated with the ES */
static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es)
{
+ bool old_is_local;
+ bool is_local;
+
if (!CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL))
return;
+ old_is_local = bgp_evpn_is_es_local_and_non_bypass(es);
UNSET_FLAG(es->flags, BGP_EVPNES_LOCAL);
+ is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ if (old_is_local != is_local)
+ bgp_evpn_mac_update_on_es_local_chg(es, is_local);
+
/* remove from the ES local list */
list_delete_node(bgp_mh_info->local_es_list, &es->es_listnode);
|| bgp_evpn_local_es_is_active(es));
}
+static bool bgp_evpn_is_valid_local_path(struct bgp_path_info *pi)
+{
+ return (CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_STATIC);
+}
+
/* Update all local MAC-IP routes in the VNI routing table associated
* with the ES. When the ES is down the routes are advertised without
* the L3 extcomm
bgp = bgp_get_evpn();
for (ALL_LIST_ELEMENTS_RO(es->macip_evi_path_list, node, es_info)) {
pi = es_info->pi;
- if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID))
+
+ if (!bgp_evpn_is_valid_local_path(pi))
continue;
- if (pi->type != ZEBRA_ROUTE_BGP
- || pi->sub_type != BGP_ROUTE_STATIC)
+ if (!bgp_evpn_is_macip_path(pi))
continue;
vpn = bgp_evpn_lookup_vni(bgp, es_info->vni);
}
}
+static bool bgp_evpn_is_valid_bgp_path(struct bgp_path_info *pi)
+{
+ return (CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_NORMAL);
+}
+
+/* If an ES is no longer local (or becomes local) we need to re-install
+ * paths using that ES as destination. This is needed as the criteria
+ * for best path selection has changed.
+ */
+static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es,
+ bool is_local)
+{
+ struct listnode *node;
+ struct bgp_path_es_info *es_info;
+ struct bgp_path_info *pi;
+ char prefix_buf[PREFIX_STRLEN];
+ bool tmp_local;
+ struct attr *attr_new;
+ struct attr attr_tmp;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("update paths linked to es %s on chg to %s",
+ es->esi_str, is_local ? "local" : "non-local");
+
+ for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) {
+ pi = es_info->pi;
+
+ /* Consider "valid" remote routes */
+ if (!bgp_evpn_is_valid_bgp_path(pi))
+ continue;
+
+ if (!pi->attr)
+ continue;
+
+ tmp_local = !!(pi->attr->es_flags & ATTR_ES_IS_LOCAL);
+ if (tmp_local == is_local)
+ continue;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug(
+ "update path %s linked to es %s on chg to %s",
+ prefix2str(&pi->net->p, prefix_buf,
+ sizeof(prefix_buf)),
+ es->esi_str, is_local ? "local" : "non-local");
+
+ attr_tmp = *pi->attr;
+ if (is_local)
+ attr_tmp.es_flags |= ATTR_ES_IS_LOCAL;
+ else
+ attr_tmp.es_flags &= ~ATTR_ES_IS_LOCAL;
+ attr_new = bgp_attr_intern(&attr_tmp);
+ bgp_attr_unintern(&pi->attr);
+ pi->attr = attr_new;
+ bgp_evpn_import_type2_route(pi, 1);
+ }
+}
+
static void bgp_evpn_local_es_deactivate(struct bgp *bgp,
struct bgp_evpn_es *es)
{
bool old_bypass = !!(es->flags & BGP_EVPNES_BYPASS);
bool old_active;
bool new_active;
+ bool old_is_local;
+ bool is_local;
if (bypass == old_bypass)
return;
old_active = bgp_evpn_local_es_is_active(es);
+ old_is_local = bgp_evpn_is_es_local_and_non_bypass(es);
if (bypass)
SET_FLAG(es->flags, BGP_EVPNES_BYPASS);
else
else
bgp_evpn_local_es_deactivate(bgp, es);
}
+
+ is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ if (old_is_local != is_local)
+ bgp_evpn_mac_update_on_es_local_chg(es, is_local);
}
static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es)
bgp_evpn_es_local_info_clear(es);
}
-bool bgp_evpn_is_esi_local(esi_t *esi)
+bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi)
{
struct bgp_evpn_es *es = NULL;
/* Lookup ESI hash - should exist. */
es = bgp_evpn_es_find(esi);
- return es ? !!(es->flags & BGP_EVPNES_LOCAL) : false;
+
+ return es && bgp_evpn_is_es_local_and_non_bypass(es);
}
int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi)