]> git.puffer.fish Git - matthieu/frr.git/commitdiff
zebra: changes for programming SPH, non-DF and backup NHG br-port attrs
authorAnuradha Karuppiah <anuradhak@cumulusnetworks.com>
Mon, 13 Apr 2020 22:56:03 +0000 (15:56 -0700)
committerAnuradha Karuppiah <anuradhak@cumulusnetworks.com>
Mon, 26 Oct 2020 17:33:19 +0000 (10:33 -0700)
split horizon filter, non-DF block filter and backup nexthop group
are passed as bridge port attributes to the dataplane.

Signed-off-by: Anuradha Karuppiah <anuradhak@cumulusnetworks.com>
zebra/zebra_dplane.c
zebra/zebra_evpn_mh.c
zebra/zebra_evpn_mh.h

index 56545d07663c00be12b03a02b633e5ca2c47737c..d3bcffe960e158bf4a0ff40247a5e23d3c5b8331 100644 (file)
@@ -2908,7 +2908,7 @@ dplane_br_port_update(const struct interface *ifp, bool non_df,
        if (non_df)
                flags |= DPLANE_BR_PORT_NON_DF;
 
-       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL || IS_ZEBRA_DEBUG_EVPN_MH_ES) {
                uint32_t i;
                char vtep_str[ES_VTEP_LIST_STR_SZ];
 
index a6612d50f0a79ad957d45a65da5bb23e66249331..3c457bdfa5d88aea920dc8c726af7b4b0f995ec9 100644 (file)
@@ -63,6 +63,8 @@ static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es,
 static void zebra_evpn_local_es_del(struct zebra_evpn_es **esp);
 static int zebra_evpn_local_es_update(struct zebra_if *zif, uint32_t lid,
                struct ethaddr *sysmac);
+static bool zebra_evpn_es_br_port_dplane_update(struct zebra_evpn_es *es,
+                                               const char *caller);
 
 esi_t zero_esi_buf, *zero_esi = &zero_esi_buf;
 
@@ -897,12 +899,23 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es)
 
                es->flags |= ZEBRA_EVPNES_NHG_ACTIVE;
                kernel_upd_mac_nhg(es->nhg_id, nh_cnt, nh_ids);
+               if (!(es->flags & ZEBRA_EVPNES_NHG_ACTIVE)) {
+                       es->flags |= ZEBRA_EVPNES_NHG_ACTIVE;
+                       /* add backup NHG to the br-port */
+                       if ((es->flags & ZEBRA_EVPNES_LOCAL))
+                               zebra_evpn_es_br_port_dplane_update(es,
+                                                                   __func__);
+               }
        } else {
                if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) {
                        if (IS_ZEBRA_DEBUG_EVPN_MH_NH)
                                zlog_debug("es %s nhg 0x%x del",
                                                es->esi_str, es->nhg_id);
                        es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE;
+                       /* remove backup NHG from the br-port */
+                       if ((es->flags & ZEBRA_EVPNES_LOCAL))
+                               zebra_evpn_es_br_port_dplane_update(es,
+                                                                   __func__);
                        kernel_del_mac_nhg(es->nhg_id);
                }
        }
@@ -1017,7 +1030,76 @@ static struct zebra_evpn_es_vtep *zebra_evpn_es_vtep_find(
        return NULL;
 }
 
-static void zebra_evpn_es_df_change(struct zebra_evpn_es *es, bool new_non_df,
+/* flush all the dataplane br-port info associated with the ES */
+static bool zebra_evpn_es_br_port_dplane_clear(struct zebra_evpn_es *es)
+{
+       struct in_addr sph_filters[ES_VTEP_MAX_CNT];
+
+       if (!(es->flags & ZEBRA_EVPNES_BR_PORT))
+               return false;
+
+       if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+               zlog_debug("es %s br-port dplane clear", es->esi_str);
+
+       memset(&sph_filters, 0, sizeof(sph_filters));
+       dplane_br_port_update(es->zif->ifp, false /* non_df */, 0, sph_filters,
+                             0 /* backup_nhg_id */);
+       return true;
+}
+
+static inline bool
+zebra_evpn_es_br_port_dplane_update_needed(struct zebra_evpn_es *es)
+{
+       return (es->flags & ZEBRA_EVPNES_NON_DF)
+              || (es->flags & ZEBRA_EVPNES_NHG_ACTIVE)
+              || listcount(es->es_vtep_list);
+}
+
+/* returns TRUE if dplane entry was updated */
+static bool zebra_evpn_es_br_port_dplane_update(struct zebra_evpn_es *es,
+                                               const char *caller)
+{
+       uint32_t backup_nhg_id;
+       struct in_addr sph_filters[ES_VTEP_MAX_CNT];
+       struct listnode *node = NULL;
+       struct zebra_evpn_es_vtep *es_vtep;
+       uint32_t sph_filter_cnt = 0;
+
+       if (!(es->flags & ZEBRA_EVPNES_LOCAL))
+               return zebra_evpn_es_br_port_dplane_clear(es);
+
+       /* If the ES is not a bridge port there is nothing
+        * in the dataplane
+        */
+       if (!(es->flags & ZEBRA_EVPNES_BR_PORT))
+               return false;
+
+       if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+               zlog_debug("es %s br-port dplane update by %s", es->esi_str, caller);
+       backup_nhg_id = (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) ? es->nhg_id : 0;
+
+       memset(&sph_filters, 0, sizeof(sph_filters));
+       if (listcount(es->es_vtep_list) > ES_VTEP_MAX_CNT) {
+               zlog_warn("es %s vtep count %d exceeds filter cnt %d",
+                         es->esi_str, listcount(es->es_vtep_list),
+                         ES_VTEP_MAX_CNT);
+       } else {
+               for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+                       if (es_vtep->flags & ZEBRA_EVPNES_VTEP_DEL_IN_PROG)
+                               continue;
+                       sph_filters[sph_filter_cnt] = es_vtep->vtep_ip;
+                       ++sph_filter_cnt;
+               }
+       }
+
+       dplane_br_port_update(es->zif->ifp, !!(es->flags & ZEBRA_EVPNES_NON_DF),
+                             sph_filter_cnt, sph_filters, backup_nhg_id);
+
+       return true;
+}
+
+/* returns TRUE if dplane entry was updated */
+static bool zebra_evpn_es_df_change(struct zebra_evpn_es *es, bool new_non_df,
                                    const char *caller)
 {
        bool old_non_df;
@@ -1030,18 +1112,20 @@ static void zebra_evpn_es_df_change(struct zebra_evpn_es *es, bool new_non_df,
                           new_non_df ? "non-df" : "df");
 
        if (old_non_df == new_non_df)
-               return;
+               return false;
 
-       if (new_non_df) {
+       if (new_non_df)
                es->flags |= ZEBRA_EVPNES_NON_DF;
-               /* XXX - Setup a dataplane DF filter to block BUM traffic */
-       } else {
+       else
                es->flags &= ~ZEBRA_EVPNES_NON_DF;
-               /* XXX - clear the non-DF block filter */
-       }
+
+       /* update non-DF block filter in the dataplane */
+       return zebra_evpn_es_br_port_dplane_update(es, __func__);
 }
 
-static void zebra_evpn_es_run_df_election(struct zebra_evpn_es *es,
+
+/* returns TRUE if dplane entry was updated */
+static bool zebra_evpn_es_run_df_election(struct zebra_evpn_es *es,
                                          const char *caller)
 {
        struct listnode *node = NULL;
@@ -1052,10 +1136,8 @@ static void zebra_evpn_es_run_df_election(struct zebra_evpn_es *es,
         * is no need to setup the BUM block filter
         */
        if (!(es->flags & ZEBRA_EVPNES_LOCAL)
-           || !zmh_info->es_originator_ip.s_addr) {
-               zebra_evpn_es_df_change(es, new_non_df, caller);
-               return;
-       }
+           || !zmh_info->es_originator_ip.s_addr)
+               return zebra_evpn_es_df_change(es, new_non_df, caller);
 
        /* if oper-state is down DF filtering must be on. when the link comes
         * up again dataplane should block BUM till FRR has had the chance
@@ -1063,8 +1145,7 @@ static void zebra_evpn_es_run_df_election(struct zebra_evpn_es *es,
         */
        if (!(es->flags & ZEBRA_EVPNES_OPER_UP)) {
                new_non_df = true;
-               zebra_evpn_es_df_change(es, new_non_df, caller);
-               return;
+               return zebra_evpn_es_df_change(es, new_non_df, caller);
        }
 
        for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
@@ -1096,7 +1177,7 @@ static void zebra_evpn_es_run_df_election(struct zebra_evpn_es *es,
                }
        }
 
-       zebra_evpn_es_df_change(es, new_non_df, caller);
+       return zebra_evpn_es_df_change(es, new_non_df, caller);
 }
 
 static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es,
@@ -1105,6 +1186,7 @@ static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es,
 {
        struct zebra_evpn_es_vtep *es_vtep;
        bool old_esr_rxed;
+       bool dplane_updated = false;
 
        es_vtep = zebra_evpn_es_vtep_find(es, vtep_ip);
 
@@ -1129,14 +1211,18 @@ static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es,
                        es_vtep->flags &= ~ZEBRA_EVPNES_VTEP_RXED_ESR;
                es_vtep->df_alg = df_alg;
                es_vtep->df_pref = df_pref;
-               zebra_evpn_es_run_df_election(es, __func__);
+               dplane_updated = zebra_evpn_es_run_df_election(es, __func__);
        }
+       /* add the vtep to the SPH list */
+       if (!dplane_updated && (es->flags & ZEBRA_EVPNES_LOCAL))
+               zebra_evpn_es_br_port_dplane_update(es, __func__);
 }
 
 static void zebra_evpn_es_vtep_del(struct zebra_evpn_es *es,
                struct in_addr vtep_ip)
 {
        struct zebra_evpn_es_vtep *es_vtep;
+       bool dplane_updated = false;
 
        es_vtep = zebra_evpn_es_vtep_find(es, vtep_ip);
 
@@ -1144,10 +1230,15 @@ static void zebra_evpn_es_vtep_del(struct zebra_evpn_es *es,
                if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
                        zlog_debug("es %s vtep %pI4 del",
                                        es->esi_str, &vtep_ip);
+               es_vtep->flags |= ZEBRA_EVPNES_VTEP_DEL_IN_PROG;
                if (es_vtep->flags & ZEBRA_EVPNES_VTEP_RXED_ESR) {
                        es_vtep->flags &= ~ZEBRA_EVPNES_VTEP_RXED_ESR;
-                       zebra_evpn_es_run_df_election(es, __func__);
+                       dplane_updated =
+                               zebra_evpn_es_run_df_election(es, __func__);
                }
+               /* remove the vtep from the SPH list */
+               if (!dplane_updated && (es->flags & ZEBRA_EVPNES_LOCAL))
+                       zebra_evpn_es_br_port_dplane_update(es, __func__);
                zebra_evpn_es_vtep_free(es_vtep);
        }
 }
@@ -1449,7 +1540,14 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es,
                        false /* es_evi_re_reval */);
 
        /* See if the local VTEP can function as DF on the ES */
-       zebra_evpn_es_run_df_election(es, __func__);
+       if (!zebra_evpn_es_run_df_election(es, __func__)) {
+               /* check if the dplane entry needs to be re-programmed as a
+                * result of some thing other than DF status change
+                */
+               if (zebra_evpn_es_br_port_dplane_update_needed(es))
+                       zebra_evpn_es_br_port_dplane_update(es, __func__);
+       }
+
 
        /* Setup ES-EVIs for all VxLAN stretched VLANs associated with
         * the zif
@@ -1467,6 +1565,7 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp)
 {
        struct zebra_if *zif;
        struct zebra_evpn_es *es = *esp;
+       bool dplane_updated = false;
 
        if (!(es->flags & ZEBRA_EVPNES_LOCAL))
                return;
@@ -1474,7 +1573,7 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp)
        es->flags &= ~(ZEBRA_EVPNES_LOCAL | ZEBRA_EVPNES_READY_FOR_BGP);
 
        /* remove the DF filter */
-       zebra_evpn_es_run_df_election(es, __func__);
+       dplane_updated = zebra_evpn_es_run_df_election(es, __func__);
 
        /* if there any local macs referring to the ES as dest we
         * need to clear the static reference on them
@@ -1482,6 +1581,10 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp)
        zebra_evpn_es_local_mac_update(es,
                        true /* force_clear_static */);
 
+       /* flush the BUM filters and backup NHG */
+       if (!dplane_updated)
+               zebra_evpn_es_br_port_dplane_clear(es);
+
        /* clear the es from the parent interface */
        zif = es->zif;
        zif->es_info.es = NULL;
@@ -1670,6 +1773,11 @@ static int zebra_evpn_remote_es_add(esi_t *esi, struct in_addr vtep_ip,
                }
        }
 
+       if (df_alg != EVPN_MH_DF_ALG_PREF)
+               zlog_warn("remote es %s vtep %s add %s with unsupported df_alg %d",
+                          esi_to_str(esi, buf, sizeof(buf)),
+                          inet_ntoa(vtep_ip), esr_rxed ? "esr" : "", df_alg);
+
        zebra_evpn_es_vtep_add(es, vtep_ip, esr_rxed, df_alg, df_pref);
        zebra_evpn_es_remote_info_re_eval(&es);
 
index a3ed09209d19ee4fe8e81787d0637b71ccf79b8a..da3c31df950aad5e93a42541025107d786c15f73 100644 (file)
@@ -55,6 +55,10 @@ struct zebra_evpn_es {
  * VTEP is not the DF
  */
 #define ZEBRA_EVPNES_NON_DF (1 << 5)
+/* When the ES becomes a bridge port we need to activate the BUM non-DF
+ * filter, SPH filter and backup NHG for fast-failover
+ */
+#define ZEBRA_EVPNES_BR_PORT (1 << 6)
 
        /* memory used for adding the es to zmh_info->es_rb_tree */
        RB_ENTRY(zebra_evpn_es) rb_node;
@@ -127,6 +131,7 @@ struct zebra_evpn_es_vtep {
        uint32_t flags;
        /* Rxed Type-4 route from this VTEP */
 #define ZEBRA_EVPNES_VTEP_RXED_ESR (1 << 0)
+#define ZEBRA_EVPNES_VTEP_DEL_IN_PROG (1 << 1)
 
        /* memory used for adding the entry to es->es_vtep_list */
        struct listnode es_listnode;