}
}
- /* MAC-IP routes in the VNI route table are linked to the
- * destination ES
+ /* local MAC-IP routes in the VNI table are linked to
+ * the destination ES
*/
if (route_change && vpn_rt
&& (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE))
afi_t afi = 0;
safi_t safi = 0;
bool new_pi = false;
+ bool use_l3nhg = false;
+ bool is_l3nhg_active = false;
memset(pp, 0, sizeof(struct prefix));
ip_prefix_from_evpn_prefix(evp, pp);
else
attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ bgp_evpn_es_vrf_use_nhg(bgp_vrf, &parent_pi->attr->esi, &use_l3nhg,
+ &is_l3nhg_active, NULL);
+ if (use_l3nhg)
+ attr.es_flags |= ATTR_ES_L3_NHG_USE;
+ if (is_l3nhg_active)
+ attr.es_flags |= ATTR_ES_L3_NHG_ACTIVE;
+
/* Check if route entry is already present. */
for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
if (pi->extra
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);
struct bgp_path_info *pi, int install)
{
esi_t *esi;
- struct in_addr nh;
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
esi = bgp_evpn_attr_get_esi(pi->attr);
}
return true;
}
+ }
+ return false;
+}
- /* Don't import routes with ES as destination if L3NHG is in
- * use and the nexthop has not been advertised via the EAD-ES
- */
- if (pi->attr)
- nh = pi->attr->nexthop;
+/*
+ * Install or uninstall a mac-ip route in the provided vrf if
+ * there is a rt match
+ */
+int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf,
+ struct bgp_path_info *pi,
+ int install)
+{
+ int ret = 0;
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(pi->net);
+
+ /* Consider "valid" remote routes applicable for
+ * this VRF.
+ */
+ if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && 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)
+ ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi);
else
- nh.s_addr = INADDR_ANY;
- if (install && !bgp_evpn_es_vrf_import_ok(bgp_vrf, esi, nh)) {
- if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
- char esi_buf[ESI_STR_LEN];
+ ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp,
+ pi);
- zlog_debug(
- "vrf %s %s of evpn prefix %pFX skipped, nh %pI4 inactive in es %s",
- bgp_vrf->name,
- install ? "import" : "unimport", evp,
- &nh,
- esi_to_str(esi, esi_buf,
- sizeof(esi_buf)));
- }
- return true;
- }
+ if (ret)
+ flog_err(EC_BGP_EVPN_FAIL,
+ "Failed to %s EVPN %pFX route in VRF %s",
+ install ? "install" : "uninstall", evp,
+ vrf_id_to_name(bgp_vrf->vrf_id));
}
- return false;
+
+ return ret;
}
/*
for (pi = bgp_dest_get_bgp_path_info(dest); pi;
pi = pi->next) {
- /* Consider "valid" remote routes applicable for
- * this VRF.
- */
- if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID)
- && pi->type == ZEBRA_ROUTE_BGP
- && pi->sub_type == BGP_ROUTE_NORMAL))
- continue;
-
- /* don't import hosts that are locally attached
- */
- if (bgp_evpn_skip_vrf_import_of_local_es(
- bgp_vrf, evp, pi, install))
- continue;
-
- if (is_route_matching_for_vrf(bgp_vrf, pi)) {
- if (bgp_evpn_route_rmac_self_check(
- bgp_vrf, evp, pi))
- continue;
-
- if (install)
- ret = install_evpn_route_entry_in_vrf(
- bgp_vrf, evp, pi);
- else
- ret = uninstall_evpn_route_entry_in_vrf(
- bgp_vrf, evp, pi);
-
- if (ret) {
- flog_err(
- EC_BGP_EVPN_FAIL,
- "Failed to %s EVPN %pFX route in VRF %s",
- install ? "install"
- : "uninstall",
- evp,
- vrf_id_to_name(
- bgp_vrf->vrf_id));
- bgp_dest_unlock_node(rd_dest);
- bgp_dest_unlock_node(dest);
- return ret;
- }
- }
+ ret = bgp_evpn_route_entry_install_if_vrf_match(
+ bgp_vrf, pi, install);
+ if (ret)
+ return ret;
}
}
}
if (sub_type != ECOMMUNITY_ROUTE_TARGET)
continue;
+ /* non-local MAC-IP routes in the global route table are linked
+ * to the destination ES
+ */
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ bgp_evpn_path_es_link(pi, 0,
+ bgp_evpn_attr_get_esi(pi->attr));
+
/*
* macip routes (type-2) are imported into VNI and VRF tables.
* IMET route is imported into VNI table.
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_update_on_vtep_chg(struct bgp_evpn_es_vtep *es_vtep,
- bool active);
esi_t zero_esi_buf, *zero_esi = &zero_esi_buf;
static int bgp_evpn_run_consistency_checks(struct thread *t);
* removed.
*/
bgp_evpn_l3nhg_update_on_vtep_chg(es_vtep->es);
- bgp_evpn_es_path_update_on_vtep_chg(es_vtep, new_active);
-
/* 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).
+ * 1. Local MAC-IP routes in the VNI routing table are linked to the
+ * destination ES (macip_evi_path_list) for efficient updates on ES oper
+ * state changes.
+ * 2. Non-local MAC-IP routes in the global routing table are linked to
+ * the detination for efficient updates on -
+ * a. VTEP add/del - this results in a L3NHG update.
+ * b. ES-VRF add/del - this may result in the host route being migrated to
+ * L3NHG or vice versa (flat multipath list).
****************************************************************************/
void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info)
{
zlog_debug("vni %u path %pFX unlinked from es %s", es_info->vni,
&pi->net->p, es->esi_str);
- list_delete_node(es->macip_evi_path_list, &es_info->es_listnode);
+ if (es_info->vni)
+ list_delete_node(es->macip_evi_path_list,
+ &es_info->es_listnode);
+ 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 = bgp_get_evpn();
+ struct bgp *bgp_evpn;
+ struct prefix_evpn *evp;
es_info = pi->extra ? pi->extra->es_info : NULL;
/* if the esi is zero just unlink the path from the old es */
return;
}
+ bgp_evpn = bgp_get_evpn();
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);
/* link mac-ip path to the new destination ES */
es_info->es = es;
listnode_init(&es_info->es_listnode, es_info);
- listnode_add(es->macip_evi_path_list, &es_info->es_listnode);
+ if (es_info->vni)
+ listnode_add(es->macip_evi_path_list, &es_info->es_listnode);
+ else
+ listnode_add(es->macip_global_path_list, &es_info->es_listnode);
}
-/* XXX - When a remote ES is added to a VRF, routes using that as
- * a destination need to be migrated to a L2NHG and viceversa
+/* 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
+ * route in the associated VRFs. As a part of the VRF install use
+ * of l3 NHG is evaluated and this results in the
+ * attr.es_flag ATTR_ES_USE_L3_NHG being set or cleared.
*/
static void
bgp_evpn_es_path_update_on_es_vrf_chg(struct bgp_evpn_es_vrf *es_vrf,
- bool active)
-{
-}
-
-static void
-bgp_evpn_es_path_update_on_vtep_chg(struct bgp_evpn_es_vtep *es_vtep,
- bool active)
+ const char *reason)
{
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;
+ struct bgp_evpn_es *es = es_vrf->es;
if (!bgp_mh_info->host_routes_use_l3nhg)
return;
if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
- zlog_debug("update paths linked to es %s on vtep chg",
- es->esi_str);
+ zlog_debug("update paths linked to es %s on es-vrf %s %s",
+ es->esi_str, es_vrf->bgp_vrf->name, reason);
- for (ALL_LIST_ELEMENTS_RO(es->macip_evi_path_list, node, es_info)) {
+ for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) {
pi = es_info->pi;
- if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID))
- continue;
-
- 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 %pFX linked to es %s on vtep chg",
- &parent_pi->net->p, es->esi_str);
- bgp_evpn_import_route_in_vrfs(parent_pi, active ? 1 : 0);
+ "update path %pFX linked to es %s on vrf chg",
+ &pi->net->p, es->esi_str);
+ bgp_evpn_route_entry_install_if_vrf_match(es_vrf->bgp_vrf, pi,
+ 1);
}
}
/* Initialise the route list used for efficient event handling */
es->macip_evi_path_list = list_new();
listset_app_node_mem(es->macip_evi_path_list);
+ es->macip_global_path_list = list_new();
+ listset_app_node_mem(es->macip_global_path_list);
QOBJ_REG(es, bgp_evpn_es);
static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller)
{
if ((es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE))
- || listcount(es->macip_evi_path_list))
+ || listcount(es->macip_evi_path_list)
+ || listcount(es->macip_global_path_list))
return;
if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
list_delete(&es->es_vrf_list);
list_delete(&es->es_vtep_list);
list_delete(&es->macip_evi_path_list);
+ list_delete(&es->macip_global_path_list);
bgp_table_unlock(es->route_table);
/* remove the entry from various databases */
continue;
if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
- zlog_debug("update path %s linked to es %s on oper chg",
- prefix2str(&pi->net->p, prefix_buf,
- sizeof(prefix_buf)),
- es->esi_str);
+ zlog_debug(
+ "update path %d %s linked to es %s on oper chg",
+ es_info->vni,
+ prefix2str(&pi->net->p, prefix_buf,
+ sizeof(prefix_buf)),
+ es->esi_str);
bgp_evpn_update_type2_route_entry(bgp, vpn, pi->net, pi,
__func__);
listcount(es->es_vrf_list));
json_object_int_add(json, "macipPathCount",
listcount(es->macip_evi_path_list));
+ json_object_int_add(json, "macipGlobalPathCount",
+ listcount(es->macip_global_path_list));
json_object_int_add(json, "inconsistentVniVtepCount",
es->incons_evi_vtep_cnt);
if (listcount(es->es_vtep_list)) {
vty_out(vty, " Remote VNI Count: %d\n",
es->remote_es_evi_cnt);
vty_out(vty, " VRF Count: %d\n", listcount(es->es_vrf_list));
- vty_out(vty, " MACIP Path Count: %d\n",
+ vty_out(vty, " MACIP EVI Path Count: %d\n",
listcount(es->macip_evi_path_list));
+ vty_out(vty, " MACIP Global Path Count: %d\n",
+ listcount(es->macip_global_path_list));
vty_out(vty, " Inconsistent VNI VTEP Count: %d\n",
es->incons_evi_vtep_cnt);
if (es->inconsistencies) {
es_vrf->nhg_id);
bgp_evpn_l3nhg_zebra_del(es_vrf);
es_vrf->flags &= ~BGP_EVPNES_VRF_NHG_ACTIVE;
+ /* MAC-IPs can now be installed via the L3NHG */
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "l3nhg-deactivate");
}
static void bgp_evpn_l3nhg_activate(struct bgp_evpn_es_vrf *es_vrf, bool update)
es_vrf->es->esi_str, es_vrf->bgp_vrf->vrf_id,
es_vrf->nhg_id);
es_vrf->flags |= BGP_EVPNES_VRF_NHG_ACTIVE;
+ /* MAC-IPs can now be installed via the L3NHG */
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "l3nhg_activate");
}
bgp_evpn_l3nhg_zebra_add(es_vrf);
/* update paths in the VRF that may already be associated with
* this destination ES
*/
- bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, true);
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "es-vrf-create");
return es_vrf;
}
/* update paths in the VRF that may already be associated with
* this destination ES
*/
- bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, false);
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "es-vrf-delete");
XFREE(MTYPE_BGP_EVPN_ES_VRF, es_vrf);
}
bgp_evpn_es_vrf_ref(es_evi, vpn->bgp_vrf);
}
-static bool bgp_evpn_es_vrf_use_nhg(struct bgp *bgp_vrf, esi_t *esi,
- struct bgp_evpn_es **es_p,
- struct bgp_evpn_es_vrf **es_vrf_p)
+/* 1. If ES-VRF is not present install the host route with the exploded/flat
+ * multi-path list.
+ * 2. If ES-VRF is present -
+ * - if L3NHG has not been activated for the ES-VRF (this could be because
+ * all the PEs attached to the VRF are down) do not install the route
+ * in zebra.
+ * - if L3NHG has been activated install the route via that L3NHG
+ */
+void bgp_evpn_es_vrf_use_nhg(struct bgp *bgp_vrf, esi_t *esi, bool *use_l3nhg,
+ bool *is_l3nhg_active,
+ struct bgp_evpn_es_vrf **es_vrf_p)
{
struct bgp_evpn_es *es;
struct bgp_evpn_es_vrf *es_vrf;
if (!bgp_mh_info->host_routes_use_l3nhg)
- return false;
+ return;
es = bgp_evpn_es_find(esi);
if (!es)
- return false;
- if (es_p)
- *es_p = es;
+ return;
es_vrf = bgp_evpn_es_vrf_find(es, bgp_vrf);
if (!es_vrf)
- return false;
+ return;
+
+ *use_l3nhg = true;
+ if (es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE)
+ *is_l3nhg_active = true;
if (es_vrf_p)
*es_vrf_p = es_vrf;
-
- return true;
-}
-
-bool bgp_evpn_es_vrf_import_ok(struct bgp *bgp_vrf, esi_t *esi,
- struct in_addr nh)
-{
- if (!bgp_evpn_es_vrf_use_nhg(bgp_vrf, esi, NULL, NULL))
- return true;
-
- return bgp_evpn_es_is_vtep_active(esi, nh);
}
/* returns false if legacy-exploded mp needs to be used for route install */
uint32_t *nhg_p)
{
esi_t *esi;
- struct bgp_evpn_es *es = NULL;
struct bgp_evpn_es_vrf *es_vrf = NULL;
struct bgp_path_info *parent_pi;
struct bgp_node *rn;
struct prefix_evpn *evp;
struct bgp_path_info *mpinfo;
+ bool use_l3nhg = false;
+ bool is_l3nhg_active = false;
*nhg_p = 0;
if (!memcmp(esi, zero_esi, sizeof(*esi)))
return false;
+ bgp_evpn_es_vrf_use_nhg(bgp_vrf, esi, &use_l3nhg, &is_l3nhg_active,
+ &es_vrf);
+
/* L3NHG support is disabled, use legacy-exploded multipath */
- if (!bgp_evpn_es_vrf_use_nhg(bgp_vrf, esi, &es, &es_vrf))
+ if (!use_l3nhg)
return false;
/* if the NHG has not been installed we cannot install the route yet,
* return a 0-NHG to indicate that
*/
- if (!(es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE))
+ if (!is_l3nhg_active)
return true;
/* this needs to be set the v6NHG if v6route */
}
/* Display all MAC-IP VNI routes linked to an ES */
-static void bgp_evpn_show_routes_mac_ip_evi_es(struct vty *vty, esi_t *esi,
- json_object *json, int detail)
+static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi,
+ json_object *json, int detail,
+ bool global_table)
{
struct bgp_node *rn;
struct bgp_path_info *pi;
json_paths = json_object_new_array();
RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) {
+ struct list *es_list;
if (esi && memcmp(esi, &es->esi, sizeof(*esi)))
continue;
- for (ALL_LIST_ELEMENTS_RO(es->macip_evi_path_list, node,
- es_info)) {
+ if (global_table)
+ es_list = es->macip_global_path_list;
+ else
+ es_list = es->macip_evi_path_list;
+
+ for (ALL_LIST_ELEMENTS_RO(es_list, node, es_info)) {
json_object *json_path = NULL;
pi = es_info->pi;
}
}
+static void bgp_evpn_show_routes_mac_ip_evi_es(struct vty *vty, esi_t *esi,
+ json_object *json, int detail)
+{
+ return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, false);
+}
+
+static void bgp_evpn_show_routes_mac_ip_global_es(struct vty *vty, esi_t *esi,
+ json_object *json, int detail)
+{
+ return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, true);
+}
+
static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
struct vty *vty, struct in_addr vtep_ip,
json_object *json, int detail)
return CMD_SUCCESS;
}
+DEFPY_HIDDEN(
+ show_bgp_l2vpn_evpn_route_mac_ip_global_es,
+ show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd,
+ "show bgp l2vpn evpn route mac-ip-global-es [NAME$esi_str|detail$detail] [json$uj]",
+ SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR
+ "EVPN route information\n"
+ "MAC IP routes in the global table linked to the ES\n"
+ "ES ID\n"
+ "Detailed information\n" JSON_STR)
+{
+ esi_t esi;
+ esi_t *esi_p;
+ json_object *json = NULL;
+
+ if (esi_str) {
+ if (!str_to_esi(esi_str, &esi)) {
+ vty_out(vty, "%%Malformed ESI\n");
+ return CMD_WARNING;
+ }
+ esi_p = &esi;
+ } else {
+ esi_p = NULL;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+ bgp_evpn_show_routes_mac_ip_global_es(vty, esi_p, json, !!detail);
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+
+ return CMD_SUCCESS;
+}
+
/*
* Display EVPN import route-target hash table
*/
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd);
install_element(VIEW_NODE,
&show_bgp_l2vpn_evpn_route_mac_ip_evi_es_cmd);
+ install_element(VIEW_NODE,
+ &show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd);