]> git.puffer.fish Git - matthieu/frr.git/commitdiff
zebra: changes to run DF election
authorAnuradha Karuppiah <anuradhak@cumulusnetworks.com>
Fri, 8 May 2020 23:41:43 +0000 (16:41 -0700)
committerAnuradha Karuppiah <anuradhak@cumulusnetworks.com>
Mon, 26 Oct 2020 17:32:49 +0000 (10:32 -0700)
1. DF preference is configurable per-ES
!
interface hostbond1
 evpn mh es-df-pref 100 >>>>>>>>>>>
 evpn mh es-id 1
 evpn mh es-sys-mac 00:00:00:00:01:11
!
2. This parameter is sent to BGP and advertised via the ESR.
3. The peer-ESs' DF params are sent to zebra (by BGP) and used
for running the DF election.
4. If the local VTEP becomes non-DF on an ES a block filter is
programmed in the dataplane to drop de-capsulated BUM packets
destined to that ES.

Sample output
=============
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
torm-11# sh evpn es
Type: L local, R remote, N non-DF
ESI                            Type ES-IF                 VTEPs
03:00:00:00:00:01:11:00:00:01  LRN  hostbond1             27.0.0.16
03:00:00:00:00:01:22:00:00:02  LR   hostbond2             27.0.0.16
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
torm-11# sh evpn es 03:00:00:00:00:01:11:00:00:01
ESI: 03:00:00:00:00:01:11:00:00:01
 Type: Local,Remote
 Interface: hostbond1
 State: up
 Ready for BGP: yes
 VNI Count: 10
 MAC Count: 2
 DF: status: non-df preference: 100 >>>>>>>>
 Nexthop group: 0x2000001
 VTEPs:
     27.0.0.16 df_alg: preference df_pref: 32767 nh: 0x100000d >>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

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

index 626225836e0e802172f1da607a622bd4e7af0893..2791edf2f15834586adc294989db0816b379269a 100644 (file)
@@ -280,6 +280,7 @@ struct zebra_evpn_es;
 struct zebra_es_if_info {
        struct ethaddr sysmac;
        uint32_t lid; /* local-id; has to be unique per-ES-sysmac */
+       uint16_t df_pref;
        struct zebra_evpn_es *es; /* local ES */
 };
 
index 7dbe74e42d62cc7f702105f02d063497c107e841..a6612d50f0a79ad957d45a65da5bb23e66249331 100644 (file)
@@ -1017,10 +1017,94 @@ 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,
+                                   const char *caller)
+{
+       bool old_non_df;
+
+       old_non_df = !!(es->flags & ZEBRA_EVPNES_NON_DF);
+
+       if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+               zlog_debug("df-change(%s) es %s old %s new %s", caller,
+                          es->esi_str, old_non_df ? "non-df" : "df",
+                          new_non_df ? "non-df" : "df");
+
+       if (old_non_df == new_non_df)
+               return;
+
+       if (new_non_df) {
+               es->flags |= ZEBRA_EVPNES_NON_DF;
+               /* XXX - Setup a dataplane DF filter to block BUM traffic */
+       } else {
+               es->flags &= ~ZEBRA_EVPNES_NON_DF;
+               /* XXX - clear the non-DF block filter */
+       }
+}
+
+static void zebra_evpn_es_run_df_election(struct zebra_evpn_es *es,
+                                         const char *caller)
+{
+       struct listnode *node = NULL;
+       struct zebra_evpn_es_vtep *es_vtep;
+       bool new_non_df = false;
+
+       /* If the ES is not ready (i.e. not completely configured) there
+        * 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;
+       }
+
+       /* 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
+        * to run DF election again
+        */
+       if (!(es->flags & ZEBRA_EVPNES_OPER_UP)) {
+               new_non_df = true;
+               zebra_evpn_es_df_change(es, new_non_df, caller);
+               return;
+       }
+
+       for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+               /* Only VTEPs that have advertised the ESR can participate
+                * in DF election
+                */
+               if (!(es_vtep->flags & ZEBRA_EVPNES_VTEP_RXED_ESR))
+                       continue;
+
+               /* If the DF alg is not the same we should fall back to
+                * service-carving. But as service-carving is not supported
+                * we will stop forwarding BUM
+                */
+               if (es_vtep->df_alg != EVPN_MH_DF_ALG_PREF) {
+                       new_non_df = true;
+                       break;
+               }
+
+               /* Peer VTEP wins DF election if -
+                * the peer-VTEP has higher preference (or)
+                * the pref is the same but peer's IP address is lower
+                */
+               if ((es_vtep->df_pref > es->df_pref)
+                   || ((es_vtep->df_pref == es->df_pref)
+                       && (es_vtep->vtep_ip.s_addr
+                           < zmh_info->es_originator_ip.s_addr))) {
+                       new_non_df = true;
+                       break;
+               }
+       }
+
+       zebra_evpn_es_df_change(es, new_non_df, caller);
+}
+
 static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es,
-               struct in_addr vtep_ip)
+                                  struct in_addr vtep_ip, bool esr_rxed,
+                                  uint8_t df_alg, uint16_t df_pref)
 {
        struct zebra_evpn_es_vtep *es_vtep;
+       bool old_esr_rxed;
 
        es_vtep = zebra_evpn_es_vtep_find(es, vtep_ip);
 
@@ -1032,6 +1116,21 @@ static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es,
                /* update the L2-NHG associated with the ES */
                zebra_evpn_nh_add(es_vtep);
        }
+
+       old_esr_rxed = !!(es_vtep->flags & ZEBRA_EVPNES_VTEP_RXED_ESR);
+       if ((old_esr_rxed != esr_rxed) || (es_vtep->df_alg != df_alg)
+           || (es_vtep->df_pref != df_pref)) {
+               /* If any of the DF election params changed we need to re-run
+                * DF election
+                */
+               if (esr_rxed)
+                       es_vtep->flags |= ZEBRA_EVPNES_VTEP_RXED_ESR;
+               else
+                       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__);
+       }
 }
 
 static void zebra_evpn_es_vtep_del(struct zebra_evpn_es *es,
@@ -1045,6 +1144,10 @@ 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);
+               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__);
+               }
                zebra_evpn_es_vtep_free(es_vtep);
        }
 }
@@ -1165,15 +1268,16 @@ static int zebra_evpn_es_send_add_to_client(struct zebra_evpn_es *es)
        stream_put_ipv4(s, zmh_info->es_originator_ip.s_addr);
        oper_up = !!(es->flags & ZEBRA_EVPNES_OPER_UP);
        stream_putc(s, oper_up);
+       stream_putw(s, es->df_pref);
 
        /* Write packet size. */
        stream_putw_at(s, 0, stream_get_endp(s));
 
        if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
-               zlog_debug("send add local es %s %pI4 to %s",
-                               es->esi_str,
-                               &zmh_info->es_originator_ip,
-                               zebra_route_string(client->proto));
+               zlog_debug("send add local es %s %pI4 active %u df_pref %u to %s",
+                          es->esi_str, &zmh_info->es_originator_ip,
+                          oper_up, es->df_pref,
+                          zebra_route_string(client->proto));
 
        client->local_es_add_cnt++;
        return zserv_send_message(client, s);
@@ -1326,6 +1430,8 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es,
 
        /* attach es to interface */
        zif->es_info.es = es;
+       es->df_pref = zif->es_info.df_pref ? zif->es_info.df_pref
+                                          : EVPN_MH_DF_PREF_DEFAULT;
 
        /* attach interface to es */
        es->zif = zif;
@@ -1342,6 +1448,9 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es,
                zebra_evpn_es_re_eval_send_to_client(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__);
+
        /* Setup ES-EVIs for all VxLAN stretched VLANs associated with
         * the zif
         */
@@ -1364,6 +1473,9 @@ 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__);
+
        /* if there any local macs referring to the ES as dest we
         * need to clear the static reference on them
         */
@@ -1534,14 +1646,18 @@ static void zebra_evpn_remote_es_flush(struct zebra_evpn_es **esp)
        zebra_evpn_es_remote_info_re_eval(esp);
 }
 
-static int zebra_evpn_remote_es_add(esi_t *esi, struct in_addr vtep_ip)
+static int zebra_evpn_remote_es_add(esi_t *esi, struct in_addr vtep_ip,
+                                   bool esr_rxed, uint8_t df_alg,
+                                   uint16_t df_pref)
 {
        char buf[ESI_STR_LEN];
        struct zebra_evpn_es *es;
 
        if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
-               zlog_debug("remote es %s vtep %pI4 add",
-                               esi_to_str(esi, buf, sizeof(buf)), &vtep_ip);
+               zlog_debug("remote es %s vtep %pI4 add %s df_alg %d df_pref %d",
+                          esi_to_str(esi, buf, sizeof(buf)),
+                          &vtep_ip, esr_rxed ? "esr" : "", df_alg,
+                          df_pref);
 
        es = zebra_evpn_es_find(esi);
        if (!es) {
@@ -1554,7 +1670,7 @@ static int zebra_evpn_remote_es_add(esi_t *esi, struct in_addr vtep_ip)
                }
        }
 
-       zebra_evpn_es_vtep_add(es, vtep_ip);
+       zebra_evpn_es_vtep_add(es, vtep_ip, esr_rxed, df_alg, df_pref);
        zebra_evpn_es_remote_info_re_eval(&es);
 
        return 0;
@@ -1579,10 +1695,22 @@ void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS)
        stream_get(&esi, s, sizeof(esi_t));
        vtep_ip.s_addr = stream_get_ipv4(s);
 
-       if (hdr->command == ZEBRA_REMOTE_ES_VTEP_ADD)
-               zebra_evpn_remote_es_add(&esi, vtep_ip);
-       else
+       if (hdr->command == ZEBRA_REMOTE_ES_VTEP_ADD) {
+               uint32_t zapi_flags;
+               uint8_t df_alg;
+               uint16_t df_pref;
+               bool esr_rxed;
+
+               zapi_flags = stream_getl(s);
+               esr_rxed = (zapi_flags & ZAPI_ES_VTEP_FLAG_ESR_RXED) ? true
+                                                                    : false;
+               df_alg = stream_getc(s);
+               df_pref = stream_getw(s);
+               zebra_evpn_remote_es_add(&esi, vtep_ip, esr_rxed, df_alg,
+                                        df_pref);
+       } else {
                zebra_evpn_remote_es_del(&esi, vtep_ip);
+       }
 }
 
 void zebra_evpn_es_mac_deref_entry(zebra_mac_t *mac)
@@ -1708,6 +1836,35 @@ void zebra_evpn_es_cleanup(void)
        }
 }
 
+static void zebra_evpn_es_df_pref_update(struct zebra_if *zif, uint16_t df_pref)
+{
+       struct zebra_evpn_es *es;
+       uint16_t tmp_pref;
+
+       if (zif->es_info.df_pref == df_pref)
+               return;
+
+       zif->es_info.df_pref = df_pref;
+       es = zif->es_info.es;
+
+       if (!es)
+               return;
+
+       tmp_pref = zif->es_info.df_pref ? zif->es_info.df_pref
+                                       : EVPN_MH_DF_PREF_DEFAULT;
+
+       if (es->df_pref == tmp_pref)
+               return;
+
+       es->df_pref = tmp_pref;
+       /* run df election */
+       zebra_evpn_es_run_df_election(es, __func__);
+       /* notify bgp */
+       if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP)
+               zebra_evpn_es_send_add_to_client(es);
+}
+
+
 /* Only certain types of access ports can be setup as an Ethernet Segment */
 bool zebra_evpn_is_if_es_capable(struct zebra_if *zif)
 {
@@ -1746,6 +1903,8 @@ void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up)
        else
                es->flags &= ~ZEBRA_EVPNES_OPER_UP;
 
+       zebra_evpn_es_run_df_election(es, __func__);
+
        /* inform BGP of the ES oper state change */
        if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP)
                zebra_evpn_es_send_add_to_client(es);
@@ -1790,6 +1949,8 @@ static void zebra_evpn_es_show_entry(struct vty *vty,
                        strlcat(type_str, "L", sizeof(type_str));
                if (es->flags & ZEBRA_EVPNES_REMOTE)
                        strlcat(type_str, "R", sizeof(type_str));
+               if (es->flags & ZEBRA_EVPNES_NON_DF)
+                       strlcat(type_str, "N", sizeof(type_str));
 
                zebra_evpn_es_vtep_str(vtep_str, es, sizeof(vtep_str));
 
@@ -1804,7 +1965,8 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty,
                struct zebra_evpn_es *es, json_object *json)
 {
        char type_str[80];
-       struct zebra_evpn_es_vtep *zvtep;
+       char alg_buf[EVPN_DF_ALG_STR_LEN];
+       struct zebra_evpn_es_vtep *es_vtep;
        struct listnode *node;
 
        if (json) {
@@ -1832,12 +1994,22 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty,
                                "yes" : "no");
                vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list));
                vty_out(vty, " MAC Count: %d\n", listcount(es->mac_list));
+               vty_out(vty, " DF: status: %s preference: %u\n",
+                       (es->flags & ZEBRA_EVPNES_NON_DF) ? "non-df" : "df",
+                       es->df_pref);
                vty_out(vty, " Nexthop group: 0x%x\n", es->nhg_id);
                vty_out(vty, " VTEPs:\n");
-               for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, zvtep))
-                       vty_out(vty, "     %pI4 nh: 0x%x\n",
-                                       &zvtep->vtep_ip,
-                                       zvtep->nh_id);
+               for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+                       vty_out(vty, "     %pI4",
+                                       &es_vtep->vtep_ip);
+                       if (es_vtep->flags & ZEBRA_EVPNES_VTEP_RXED_ESR)
+                               vty_out(vty, " df_alg: %s df_pref: %d",
+                                       evpn_es_df_alg2str(es_vtep->df_alg,
+                                                          alg_buf,
+                                                          sizeof(alg_buf)),
+                                       es_vtep->df_pref);
+                       vty_out(vty, " nh: 0x%x\n", es_vtep->nh_id);
+               }
 
                vty_out(vty, "\n");
        }
@@ -1851,7 +2023,7 @@ void zebra_evpn_es_show(struct vty *vty, bool uj)
        if (uj) {
                /* XXX */
        } else {
-               vty_out(vty, "Type: L local, R remote\n");
+               vty_out(vty, "Type: L local, R remote, N non-DF\n");
                vty_out(vty, "%-30s %-4s %-21s %s\n",
                                "ESI", "Type", "ES-IF", "VTEPs");
        }
@@ -1898,12 +2070,41 @@ int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp)
                vty_out(vty, " evpn mh es-sys-mac %s\n",
                                prefix_mac2str(&zif->es_info.sysmac,
                                        buf, sizeof(buf)));
+
+       if (zif->es_info.df_pref)
+               vty_out(vty, " evpn mh es-df-pref %u\n", zif->es_info.df_pref);
+
        return 0;
 }
 
 #ifndef VTYSH_EXTRACT_PL
 #include "zebra/zebra_evpn_mh_clippy.c"
 #endif
+/* CLI for configuring DF preference part for an ES */
+DEFPY(zebra_evpn_es_pref, zebra_evpn_es_pref_cmd,
+      "[no$no] evpn mh es-df-pref [(1-65535)$df_pref]",
+      NO_STR "EVPN\n" EVPN_MH_VTY_STR
+            "preference value used for DF election\n"
+            "ID\n")
+{
+       VTY_DECLVAR_CONTEXT(interface, ifp);
+       struct zebra_if *zif;
+
+       zif = ifp->info;
+
+       if (no) {
+               zebra_evpn_es_df_pref_update(zif, 0);
+       } else {
+               if (!zebra_evpn_is_if_es_capable(zif)) {
+                       vty_out(vty,
+                               "%%DF preference cannot be associated with this interface type\n");
+                       return CMD_WARNING;
+               }
+               zebra_evpn_es_df_pref_update(zif, df_pref);
+       }
+       return CMD_SUCCESS;
+}
+
 /* CLI for setting up sysmac part of ESI on an access port */
 DEFPY(zebra_evpn_es_sys_mac,
       zebra_evpn_es_sys_mac_cmd,
@@ -2037,6 +2238,8 @@ void zebra_evpn_es_set_base_evpn(zebra_evpn_t *zevpn)
 
        /* if originator ip changes we need to update bgp */
        for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, node, es)) {
+               zebra_evpn_es_run_df_election(es, __func__);
+
                if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP)
                        zebra_evpn_es_send_add_to_client(es);
                else
@@ -2136,6 +2339,7 @@ void zebra_evpn_interface_init(void)
 {
        install_element(INTERFACE_NODE, &zebra_evpn_es_id_cmd);
        install_element(INTERFACE_NODE, &zebra_evpn_es_sys_mac_cmd);
+       install_element(INTERFACE_NODE, &zebra_evpn_es_pref_cmd);
 }
 
 void zebra_evpn_mh_init(void)
index 72b7f9b67507f813286b5f7deb239fed027aaa75..a3ed09209d19ee4fe8e81787d0637b71ccf79b8a 100644 (file)
@@ -51,6 +51,10 @@ struct zebra_evpn_es {
 #define ZEBRA_EVPNES_OPER_UP       (1 << 2) /* es->ifp is oper-up */
 #define ZEBRA_EVPNES_READY_FOR_BGP (1 << 3) /* ready to be sent to BGP */
 #define ZEBRA_EVPNES_NHG_ACTIVE    (1 << 4) /* NHG has been installed */
+/* This flag is only applicable to local ESs and signifies that this
+ * VTEP is not the DF
+ */
+#define ZEBRA_EVPNES_NON_DF (1 << 5)
 
        /* memory used for adding the es to zmh_info->es_rb_tree */
        RB_ENTRY(zebra_evpn_es) rb_node;
@@ -74,6 +78,11 @@ struct zebra_evpn_es {
 
        /* Nexthop group id */
        uint32_t nhg_id;
+
+       /* Preference config for BUM-DF election. Sent to BGP and
+        * advertised via the ESR
+        */
+       uint16_t df_pref;
 };
 RB_HEAD(zebra_es_rb_head, zebra_evpn_es);
 RB_PROTOTYPE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp);
@@ -115,12 +124,20 @@ struct zebra_evpn_es_vtep {
        struct zebra_evpn_es *es; /* parent ES */
        struct in_addr vtep_ip;
 
+       uint32_t flags;
+       /* Rxed Type-4 route from this VTEP */
+#define ZEBRA_EVPNES_VTEP_RXED_ESR (1 << 0)
+
        /* memory used for adding the entry to es->es_vtep_list */
        struct listnode es_listnode;
 
        /* MAC nexthop */
        uint32_t nh_id;
 
+       /* Parameters for DF election */
+       uint8_t df_alg;
+       uint32_t df_pref;
+
        /* XXX - maintain a backpointer to zebra_vtep_t */
 };