]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: handle local ES del or transition to LACP bypass
authorAnuradha Karuppiah <anuradhak@cumulusnetworks.com>
Sun, 30 Aug 2020 21:05:33 +0000 (14:05 -0700)
committerAnuradha Karuppiah <anuradhak@nvidia.com>
Fri, 26 Mar 2021 02:24:39 +0000 (19:24 -0700)
1. When a local ES is deleted or the ES-bond goes into bypass we treat
imported MAC-IP routes with that ES destination as remote routes instead
of sync routes. This requires a re-evaluation of the routes as
"non-local-dest" and an update to zebra.
2. When a ES is attached to an access port or the ES-bond transitions from
bypass to LACP-up we treat imported MAC-IP routes with that ES destination as
sync routes. This requires a re-evaluation of the routes as
"local-dest" and an update to zebra.

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

index dfc22606e7cd4fcb0de1d44a65a78399a615f396..2d4fea413a1a4f1fc9c189d5f6ac55edd0decd0f 100644 (file)
@@ -1671,7 +1671,7 @@ static inline bool bgp_evpn_route_add_l3_ecomm_ok(struct bgpevpn *vpn,
        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);
 }
@@ -2498,6 +2498,8 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
        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
@@ -2520,6 +2522,7 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
                /* 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)) {
@@ -2538,6 +2541,23 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
                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;
@@ -2553,10 +2573,9 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
         * 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;
@@ -2908,15 +2927,14 @@ int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf,
              && 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,
@@ -3169,11 +3187,9 @@ static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi,
                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,
@@ -3374,6 +3390,18 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
                                                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)
 {
@@ -3727,7 +3755,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
        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;
index 29d3d2c62f152fcd6d2972b7f0f874ceeaac8d41..83a6dd84c82676a224d218f1269763acc3f3375c 100644 (file)
@@ -206,5 +206,4 @@ extern void bgp_evpn_init(struct bgp *bgp);
 extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx);
 extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx);
 extern void update_advertise_vrf_routes(struct bgp *bgp_vrf);
-
 #endif /* _QUAGGA_BGP_EVPN_H */
index 975a390e7912a328d58eb7f383efb4917031cf6b..dcdb1626c440572bd91df836cbc0fcacf07b5ce4 100644 (file)
@@ -71,6 +71,8 @@ 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_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);
@@ -1439,6 +1441,7 @@ static void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info)
        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
@@ -1456,7 +1459,6 @@ 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;
-       struct prefix_evpn *evp;
 
        es_info = (pi->extra && pi->extra->mh_info)
                          ? pi->extra->mh_info->es_info
@@ -1472,14 +1474,6 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi)
        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);
@@ -1509,6 +1503,18 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi)
                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
@@ -1535,6 +1541,9 @@ bgp_evpn_es_path_update_on_es_vrf_chg(struct bgp_evpn_es_vrf *es_vrf,
        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",
@@ -1637,15 +1646,25 @@ static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller)
        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);
 
@@ -1655,16 +1674,28 @@ static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es)
        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);
 
@@ -1702,6 +1733,13 @@ bool bgp_evpn_es_add_l3_ecomm_ok(esi_t *esi)
                || 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
@@ -1725,11 +1763,11 @@ static void bgp_evpn_mac_update_on_es_oper_chg(struct bgp_evpn_es *es)
        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);
@@ -1749,6 +1787,65 @@ static void bgp_evpn_mac_update_on_es_oper_chg(struct bgp_evpn_es *es)
        }
 }
 
+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)
 {
@@ -1863,11 +1960,14 @@ static void bgp_evpn_local_es_bypass_update(struct bgp *bgp,
        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
@@ -1884,6 +1984,10 @@ static void bgp_evpn_local_es_bypass_update(struct bgp *bgp,
                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)
@@ -1911,13 +2015,14 @@ 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)
index c40907c33873fea4cbfbfb6140080fe2877ba0c3..c96de8687159895b3e05a32f89be9f1f002b9630 100644 (file)
@@ -403,8 +403,8 @@ void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni,
                bool uj, bool detail);
 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 bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi);
 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);
index dfaac76f02877e033f942217ac1646e12276f4d5..debed9f68be51cc9b4d4c788bfca483e1ff5011e 100644 (file)
@@ -639,4 +639,5 @@ extern void bgp_evpn_update_type2_route_entry(struct bgp *bgp,
 extern int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf,
                                                     struct bgp_path_info *pi,
                                                     int install);
+extern void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import);
 #endif /* _BGP_EVPN_PRIVATE_H */
index 8ee25e9532d90e1571eb51bea4a64973e6b1bdf1..b6aa53070b7286fc7e0ce75abfddf152ce306d10 100644 (file)
@@ -115,6 +115,11 @@ struct bgp_path_es_info {
        struct bgp_evpn_es *es;
        /* memory used for linking the path to the destination ES */
        struct listnode es_listnode;
+       uint8_t flags;
+/* Path is linked to the VNI list */
+#define BGP_EVPN_PATH_ES_INFO_VNI_LIST (1 << 0)
+/* Path is linked to the global list */
+#define BGP_EVPN_PATH_ES_INFO_GLOBAL_LIST (1 << 1)
 };
 
 /* IP paths imported into the VRF from an EVPN route source