]> git.puffer.fish Git - matthieu/frr.git/commitdiff
zebra: support for slow-failover of local MACs on an ES
authorAnuradha Karuppiah <anuradhak@cumulusnetworks.com>
Sat, 9 May 2020 01:47:52 +0000 (18:47 -0700)
committerAnuradha Karuppiah <anuradhak@nvidia.com>
Tue, 1 Dec 2020 17:46:26 +0000 (09:46 -0800)
When a local ES flaps there are two modes in which the local
MACs are failed over -
1. Fast failover - A backup NHG (ES-peer group) is programmed in the
dataplane per-access port. When a local ES flaps the MAC entries
are left unaltered i.e. pointing to the down access port. And the
dataplane redirects traffic destined to the oper-down access port
via the backup NHG.
2. Slow failover - This mode needs to be turned on to allow dataplanes
not capable of re-directing traffic. In this mode local MAC entries
on a down local ES are re-programmed to point to the ES-peers'
NHG. And vice-versa i.e. when the ES comes up the MAC entries
are re-programmed with the access port as dest.

Fast failover is on by default. Slow failover can be enabled via the
following config -
evpn mh redirect-off

Signed-off-by: Anuradha Karuppiah <anuradhak@cumulusnetworks.com>
zebra/rt_netlink.c
zebra/zebra_evpn_mac.c
zebra/zebra_evpn_mac.h
zebra/zebra_evpn_mh.c
zebra/zebra_evpn_mh.h
zebra/zebra_vty.c
zebra/zebra_vxlan.c
zebra/zebra_vxlan.h

index b737208f4718f978e113947e1260332887ec3046..f59fbae3afebb6af2c5b4c4976bd5572c463c0f2 100644 (file)
@@ -2956,8 +2956,9 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
                }
 
                if (IS_ZEBRA_IF_VXLAN(ifp))
-                       return zebra_vxlan_check_del_local_mac(ifp, br_if, &mac,
-                                                              vid);
+                       return zebra_vxlan_dp_network_mac_add(
+                               ifp, br_if, &mac, vid, nhg_id, sticky,
+                               !!(ndm->ndm_flags & NTF_EXT_LEARNED));
 
                return zebra_vxlan_local_mac_add_update(ifp, br_if, &mac, vid,
                                sticky, local_inactive, dp_static);
@@ -2985,8 +2986,7 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
        }
 
        if (IS_ZEBRA_IF_VXLAN(ifp))
-               return zebra_vxlan_check_readd_remote_mac(ifp, br_if, &mac,
-                                                         vid);
+               return zebra_vxlan_dp_network_mac_del(ifp, br_if, &mac, vid);
 
        return zebra_vxlan_local_mac_del(ifp, br_if, &mac, vid);
 }
index f1e289ec60231bd131cd5ea109bcb5ae851c4537..0b0670f079e334f741d9c215109b5b581e1fba40 100644 (file)
@@ -108,9 +108,6 @@ int zebra_evpn_rem_mac_install(zebra_evpn_t *zevpn, zebra_mac_t *mac,
        uint32_t nhg_id;
        struct in_addr vtep_ip;
 
-       if (!(mac->flags & ZEBRA_MAC_REMOTE))
-               return 0;
-
        zif = zevpn->vxlan_if->info;
        if (!zif)
                return -1;
@@ -165,9 +162,6 @@ int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevpn, zebra_mac_t *mac,
        vlanid_t vid;
        enum zebra_dplane_result res;
 
-       if (!(mac->flags & ZEBRA_MAC_REMOTE))
-               return 0;
-
        /* If the MAC was not installed there is no need to uninstall it */
        if (!force && mac->es && !(mac->es->flags & ZEBRA_EVPNES_NHG_ACTIVE))
                return -1;
@@ -1187,10 +1181,9 @@ struct hash *zebra_mac_db_create(const char *desc)
 }
 
 /* program sync mac flags in the dataplane  */
-void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
-                                   bool force_clear_static, const char *caller)
+int zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
+                                  bool force_clear_static, const char *caller)
 {
-       char macbuf[ETHER_ADDR_STRLEN];
        struct interface *ifp;
        bool sticky;
        bool set_static;
@@ -1205,13 +1198,11 @@ void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
        if (!ifp) {
                if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
                        zlog_debug(
-                               "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no access-port",
-                               caller, zevpn->vni,
-                               prefix_mac2str(&mac->macaddr, macbuf,
-                                              sizeof(macbuf)),
+                               "%s: dp-install sync-mac vni %u mac %pEA es %s 0x%x %sskipped, no access-port",
+                               caller, zevpn->vni, &mac->macaddr,
                                mac->es ? mac->es->esi_str : "-", mac->flags,
                                set_inactive ? "inactive " : "");
-               return;
+               return -1;
        }
 
        zif = ifp->info;
@@ -1219,13 +1210,11 @@ void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
        if (!br_ifp) {
                if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
                        zlog_debug(
-                               "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no br",
-                               caller, zevpn->vni,
-                               prefix_mac2str(&mac->macaddr, macbuf,
-                                              sizeof(macbuf)),
+                               "%s: dp-install sync-mac vni %u mac %pEA es %s 0x%x %sskipped, no br",
+                               caller, zevpn->vni, &mac->macaddr,
                                mac->es ? mac->es->esi_str : "-", mac->flags,
                                set_inactive ? "inactive " : "");
-               return;
+               return -1;
        }
 
        sticky = !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY);
@@ -1234,17 +1223,42 @@ void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
        else
                set_static = zebra_evpn_mac_is_static(mac);
 
+       /* We can install a local mac that has been synced from the peer
+        * over the VxLAN-overlay/network-port if fast failover is not
+        * supported and if the local ES is oper-down.
+        */
+       if (mac->es && zebra_evpn_es_local_mac_via_network_port(mac->es)) {
+               if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+                       zlog_debug(
+                               "dp-%s sync-nw-mac vni %u mac %pEA es %s 0x%x %s",
+                               set_static ? "install" : "uninstall",
+                               zevpn->vni, &mac->macaddr,
+                               mac->es ? mac->es->esi_str : "-", mac->flags,
+                               set_inactive ? "inactive " : "");
+               if (set_static)
+                       /* XXX - old_static needs to be computed more
+                        * accurately
+                        */
+                       zebra_evpn_rem_mac_install(zevpn, mac,
+                                                  true /* old_static */);
+               else
+                       zebra_evpn_rem_mac_uninstall(zevpn, mac,
+                                                    false /* force */);
+
+               return 0;
+       }
+
        if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
                zlog_debug(
-                       "dp-install sync-mac vni %u mac %s es %s 0x%x %s%s",
-                       zevpn->vni,
-                       prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
+                       "dp-install sync-mac vni %u mac %pEA es %s 0x%x %s%s",
+                       zevpn->vni, &mac->macaddr,
                        mac->es ? mac->es->esi_str : "-", mac->flags,
                        set_static ? "static " : "",
                        set_inactive ? "inactive " : "");
 
        dplane_local_mac_add(ifp, br_ifp, vid, &mac->macaddr, sticky,
                             set_static, set_inactive);
+       return 0;
 }
 
 void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready,
index b837259228eea45e8577a7e4aacc0d377cd2310d..1730991f1e7ad8dc423b609bbfebd204323c1a26 100644 (file)
@@ -173,7 +173,6 @@ struct sync_mac_ip_ctx {
        zebra_mac_t *mac;
 };
 
-/**************************** SYNC MAC handling *****************************/
 /**************************** SYNC MAC handling *****************************/
 /* if the mac has been added of a mac-route from the peer
  * or if it is being referenced by a neigh added by the
@@ -219,9 +218,8 @@ int zebra_evpn_macip_send_msg_to_client(uint32_t id, struct ethaddr *macaddr,
 void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json);
 void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt);
 void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt);
-void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
-                                   bool force_clear_static,
-                                   const char *caller);
+int zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
+                                  bool force_clear_static, const char *caller);
 void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready,
                                           bool new_bgp_ready);
 
index 8c639dc3248b4742c7d2c337fbdf1d05d7be23a4..33b2cd6a47bb90efc6a0f64842e491f18891c727 100644 (file)
@@ -965,46 +965,136 @@ void zebra_evpn_if_cleanup(struct zebra_if *zif)
  *   A L2 NH entry is programmed in the kernel for every ES-VTEP entry. This
  * NH is then added to the L2-ECMP-NHG associated with the ES.
  */
-static uint32_t zebra_evpn_nhid_alloc(bool is_nhg)
+static uint32_t zebra_evpn_nhid_alloc(struct zebra_evpn_es *es)
 {
        uint32_t id;
-       int type;
+       uint32_t nh_id;
 
        bf_assign_index(zmh_info->nh_id_bitmap, id);
 
        if (!id)
                return 0;
 
-       type = is_nhg ? EVPN_NHG_ID_TYPE_BIT : EVPN_NH_ID_TYPE_BIT;
-       return (id | type);
+       if (es) {
+               nh_id = id | EVPN_NHG_ID_TYPE_BIT;
+               /* Add to NHG hash */
+               es->nhg_id = nh_id;
+               if (!hash_get(zmh_info->nhg_table, es, hash_alloc_intern)) {
+                       bf_release_index(zmh_info->nh_id_bitmap, id);
+                       return 0;
+               }
+       } else {
+               nh_id = id | EVPN_NH_ID_TYPE_BIT;
+       }
+
+       return nh_id;
 }
 
-static void zebra_evpn_nhid_free(uint32_t nh_id)
+static void zebra_evpn_nhid_free(uint32_t nh_id, struct zebra_evpn_es *es)
 {
        uint32_t id = (nh_id & EVPN_NH_ID_VAL_MASK);
 
        if (!id)
                return;
 
+       if (es) {
+               hash_release(zmh_info->nhg_table, es);
+               es->nhg_id = 0;
+       }
+
        bf_release_index(zmh_info->nh_id_bitmap, id);
 }
 
+static unsigned int zebra_evpn_nhg_hash_keymake(const void *p)
+{
+       const struct zebra_evpn_es *es = p;
+
+       return jhash_1word(es->nhg_id, 0);
+}
+
+static bool zebra_evpn_nhg_cmp(const void *p1, const void *p2)
+{
+       const struct zebra_evpn_es *es1 = p1;
+       const struct zebra_evpn_es *es2 = p2;
+
+       if (es1 == NULL && es2 == NULL)
+               return true;
+
+       if (es1 == NULL || es2 == NULL)
+               return false;
+
+       return (es1->nhg_id == es2->nhg_id);
+}
+
+/* Lookup ES using the NHG id associated with it */
+static struct zebra_evpn_es *zebra_evpn_nhg_find(uint32_t nhg_id)
+{
+       struct zebra_evpn_es *es;
+       struct zebra_evpn_es tmp;
+
+       tmp.nhg_id = nhg_id;
+       es = hash_lookup(zmh_info->nhg_table, &tmp);
+
+       return es;
+}
+
+/* Returns TRUE if the NHG is associated with a local ES */
+bool zebra_evpn_nhg_is_local_es(uint32_t nhg_id,
+                               struct zebra_evpn_es **local_es)
+{
+       struct zebra_evpn_es *es;
+
+       es = zebra_evpn_nhg_find(nhg_id);
+       if (es && (es->flags & ZEBRA_EVPNES_LOCAL)) {
+               *local_es = es;
+               return true;
+       }
+
+       *local_es = NULL;
+       return false;
+}
+
 /* update remote macs associated with the ES */
 static void zebra_evpn_nhg_mac_update(struct zebra_evpn_es *es)
 {
        zebra_mac_t *mac;
        struct listnode *node;
+       bool local_via_nw;
 
-       for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) {
-               if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
-                       continue;
+       local_via_nw = zebra_evpn_es_local_mac_via_network_port(es);
+       if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+               zlog_debug("mac update on es %s nhg %s", es->esi_str,
+                          (es->flags & ZEBRA_EVPNES_NHG_ACTIVE)
+                                  ? "activate"
+                                  : "de-activate");
 
-               if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE)
-                       zebra_evpn_rem_mac_install(mac->zevpn, mac,
-                                                  false /*was_static*/);
-               else
-                       zebra_evpn_rem_mac_uninstall(mac->zevpn, mac,
-                                                    true /*force*/);
+       for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) {
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
+                   || (local_via_nw && CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
+                       && zebra_evpn_mac_is_static(mac))) {
+                       if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) {
+                               if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+                                       zlog_debug(
+                                               "%smac %pEA install via es %s nhg 0x%x",
+                                               (mac->flags & ZEBRA_MAC_REMOTE)
+                                                       ? "rem"
+                                                       : "local-nw",
+                                               &mac->macaddr, es->esi_str,
+                                               es->nhg_id);
+                               zebra_evpn_rem_mac_install(
+                                       mac->zevpn, mac, false /*was_static*/);
+                       } else {
+                               if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+                                       zlog_debug(
+                                               "%smac %pEA un-install es %s",
+                                               (mac->flags & ZEBRA_MAC_REMOTE)
+                                                       ? "rem"
+                                                       : "local-nw",
+                                               &mac->macaddr, es->esi_str);
+                               zebra_evpn_rem_mac_uninstall(mac->zevpn, mac,
+                                                            true /*force*/);
+                       }
+               }
        }
 }
 
@@ -1078,7 +1168,7 @@ static void zebra_evpn_nh_add(struct zebra_evpn_es_vtep *es_vtep)
        if (es_vtep->nh_id)
                return;
 
-       es_vtep->nh_id = zebra_evpn_nhid_alloc(false);
+       es_vtep->nh_id = zebra_evpn_nhid_alloc(NULL /*NHG-es*/);
 
        if (!es_vtep->nh_id)
                return;
@@ -1110,8 +1200,7 @@ static void zebra_evpn_nh_del(struct zebra_evpn_es_vtep *es_vtep)
        zebra_evpn_nhg_update(es_vtep->es);
        /* uninstall the NH */
        kernel_del_mac_nh(nh_id);
-       zebra_evpn_nhid_free(nh_id);
-
+       zebra_evpn_nhid_free(nh_id, NULL /*NHG-es*/);
 }
 
 /*****************************************************************************/
@@ -1441,7 +1530,7 @@ static struct zebra_evpn_es *zebra_evpn_es_new(esi_t *esi)
        listset_app_node_mem(es->mac_list);
 
        /* reserve a NHG  */
-       es->nhg_id = zebra_evpn_nhid_alloc(true);
+       es->nhg_id = zebra_evpn_nhid_alloc(es);
 
        if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
                zlog_debug("es %s nhg %u new", es->esi_str, es->nhg_id);
@@ -1473,7 +1562,7 @@ static void zebra_evpn_es_free(struct zebra_evpn_es **esp)
                es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE;
                kernel_del_mac_nhg(es->nhg_id);
        }
-       zebra_evpn_nhid_free(es->nhg_id);
+       zebra_evpn_nhid_free(es->nhg_id, es);
 
        /* cleanup resources maintained against the ES */
        list_delete(&es->es_evi_list);
@@ -2313,6 +2402,7 @@ void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up)
                es->flags &= ~ZEBRA_EVPNES_OPER_UP;
 
        zebra_evpn_es_run_df_election(es, __func__);
+       zebra_evpn_local_mac_oper_state_change(es);
 
        /* inform BGP of the ES oper state change */
        if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP)
@@ -3218,6 +3308,9 @@ void zebra_evpn_mh_config_write(struct vty *vty)
        if (zmh_info->startup_delay_time != ZEBRA_EVPN_MH_STARTUP_DELAY_DEF)
                vty_out(vty, "evpn mh startup-delay %d\n",
                        zmh_info->startup_delay_time);
+
+       if (zmh_info->flags & ZEBRA_EVPN_MH_REDIRECT_OFF)
+               vty_out(vty, "evpn mh redirect-off\n");
 }
 
 int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty,
@@ -3254,6 +3347,19 @@ int zebra_evpn_mh_startup_delay_update(struct vty *vty, uint32_t duration,
        return 0;
 }
 
+int zebra_evpn_mh_redirect_off(struct vty *vty, bool redirect_off)
+{
+       /* This knob needs to be set before ESs are configured
+        * i.e. cannot be changed on the fly
+        */
+       if (redirect_off)
+               zmh_info->flags |= ZEBRA_EVPN_MH_REDIRECT_OFF;
+       else
+               zmh_info->flags &= ~ZEBRA_EVPN_MH_REDIRECT_OFF;
+
+       return 0;
+}
+
 void zebra_evpn_interface_init(void)
 {
        install_element(INTERFACE_NODE, &zebra_evpn_es_id_cmd);
@@ -3275,6 +3381,8 @@ void zebra_evpn_mh_init(void)
 
        bf_init(zmh_info->nh_id_bitmap, EVPN_NH_ID_MAX);
        bf_assign_zero_index(zmh_info->nh_id_bitmap);
+       zmh_info->nhg_table = hash_create(zebra_evpn_nhg_hash_keymake,
+                                         zebra_evpn_nhg_cmp, "l2 NHG table");
 
        /* setup broadcast domain tables */
        zmh_info->evpn_vlan_table = hash_create(zebra_evpn_acc_vl_hash_keymake,
index c232e3b8f251302268f95f576b690d68e6daf170..701e5faa956ac2f0c00b74bca06df613602016dc 100644 (file)
@@ -165,6 +165,11 @@ struct zebra_evpn_access_bd {
 #define zmh_info (zrouter.mh_info)
 struct zebra_evpn_mh_info {
        uint32_t flags;
+/* If the dataplane is not capable of handling a backup NHG on an access
+ * port we will need to explicitly failover each MAC entry on
+ * local ES down
+ */
+#define ZEBRA_EVPN_MH_REDIRECT_OFF (1 << 0)
 /* DAD support for EVPN-MH is yet to be added. So on detection of
  * first local ES, DAD is turned off
  */
@@ -199,6 +204,8 @@ struct zebra_evpn_mh_info {
  */
 #define EVPN_NH_ID_TYPE_BIT  (1 << EVPN_NH_ID_TYPE_POS)
 #define EVPN_NHG_ID_TYPE_BIT (2 << EVPN_NH_ID_TYPE_POS)
+       /* L2-NHG table - key: nhg_id, data: zebra_evpn_es */
+       struct hash *nhg_table;
 
        /* XXX - re-visit the default hold timer value */
        int mac_hold_time;
@@ -234,12 +241,18 @@ static inline bool zebra_evpn_mh_is_fdb_nh(uint32_t id)
                        (id & EVPN_NH_ID_TYPE_BIT));
 }
 
+static inline bool
+zebra_evpn_es_local_mac_via_network_port(struct zebra_evpn_es *es)
+{
+       return !(es->flags & ZEBRA_EVPNES_OPER_UP)
+              && (zmh_info->flags & ZEBRA_EVPN_MH_REDIRECT_OFF);
+}
+
 static inline bool zebra_evpn_mh_do_dup_addr_detect(void)
 {
        return !(zmh_info->flags & ZEBRA_EVPN_MH_DUP_ADDR_DETECT_OFF);
 }
 
-
 /*****************************************************************************/
 extern esi_t *zero_esi;
 extern void zebra_evpn_mh_init(void);
@@ -296,5 +309,8 @@ extern bool zebra_evpn_is_es_bond(struct interface *ifp);
 extern bool zebra_evpn_is_es_bond_member(struct interface *ifp);
 extern void zebra_evpn_mh_print(struct vty *vty);
 extern void zebra_evpn_mh_json(json_object *json);
+extern bool zebra_evpn_nhg_is_local_es(uint32_t nhg_id,
+                                      struct zebra_evpn_es **local_es);
+extern int zebra_evpn_mh_redirect_off(struct vty *vty, bool redirect_off);
 
 #endif /* _ZEBRA_EVPN_MH_H */
index 8f73825700fb6d9e985a2fd0696558879c67f3e6..b209da3307a51506276b2477704db1570da74b82 100644 (file)
@@ -2497,6 +2497,20 @@ DEFPY (evpn_mh_startup_delay,
                        no ? true : false);
 }
 
+DEFPY(evpn_mh_redirect_off, evpn_mh_redirect_off_cmd,
+      "[no$no] evpn mh redirect-off",
+      NO_STR
+      "EVPN\n"
+      "Multihoming\n"
+      "ES bond redirect for fast-failover off\n")
+{
+       bool redirect_off;
+
+       redirect_off = no ? false : true;
+
+       return zebra_evpn_mh_redirect_off(vty, redirect_off);
+}
+
 DEFUN (default_vrf_vni_mapping,
        default_vrf_vni_mapping_cmd,
        "vni " CMD_VNI_RANGE "[prefix-routes-only]",
@@ -4055,6 +4069,7 @@ void zebra_vty_init(void)
        install_element(CONFIG_NODE, &evpn_mh_mac_holdtime_cmd);
        install_element(CONFIG_NODE, &evpn_mh_neigh_holdtime_cmd);
        install_element(CONFIG_NODE, &evpn_mh_startup_delay_cmd);
+       install_element(CONFIG_NODE, &evpn_mh_redirect_off_cmd);
        install_element(CONFIG_NODE, &default_vrf_vni_mapping_cmd);
        install_element(CONFIG_NODE, &no_default_vrf_vni_mapping_cmd);
        install_element(VRF_NODE, &vrf_vni_mapping_cmd);
index 64351b8072e8f0a41473224c13bb4312410144c4..09cb1cffc1c751c116b6573c39e48f7de08c919a 100644 (file)
@@ -3910,9 +3910,10 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp,
  * us, this must involve a multihoming scenario. Treat this as implicit delete
  * of any prior local MAC.
  */
-int zebra_vxlan_check_del_local_mac(struct interface *ifp,
-                                   struct interface *br_if,
-                                   struct ethaddr *macaddr, vlanid_t vid)
+static int zebra_vxlan_check_del_local_mac(struct interface *ifp,
+                                          struct interface *br_if,
+                                          struct ethaddr *macaddr,
+                                          vlanid_t vid)
 {
        struct zebra_if *zif;
        struct zebra_l2info_vxlan *vxl;
@@ -3969,14 +3970,48 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp,
        return 0;
 }
 
+/* MAC notification from the dataplane with a network dest port -
+ * 1. This can be a local MAC on a down ES (if fast-failover is not possible
+ * 2. Or it can be a remote MAC
+ */
+int zebra_vxlan_dp_network_mac_add(struct interface *ifp,
+                                  struct interface *br_if,
+                                  struct ethaddr *macaddr, vlanid_t vid,
+                                  uint32_t nhg_id, bool sticky, bool dp_static)
+{
+       struct zebra_evpn_es *es;
+       struct interface *acc_ifp;
+
+       /* if remote mac delete the local entry */
+       if (!nhg_id || !zebra_evpn_nhg_is_local_es(nhg_id, &es)
+           || !zebra_evpn_es_local_mac_via_network_port(es)) {
+               if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+                       zlog_debug("dpAdd remote MAC %pEA VID %u", macaddr,
+                                  vid);
+               return zebra_vxlan_check_del_local_mac(ifp, br_if, macaddr,
+                                                      vid);
+       }
+
+       /* If local MAC on a down local ES translate the network-mac-add
+        * to a local-inactive-mac-add
+        */
+       if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+               zlog_debug("dpAdd local-nw-MAC %pEA VID %u", macaddr, vid);
+       acc_ifp = es->zif->ifp;
+       return zebra_vxlan_local_mac_add_update(
+               acc_ifp, br_if, macaddr, vid, sticky,
+               false /* local_inactive */, dp_static);
+}
+
 /*
- * Handle remote MAC delete by kernel; readd the remote MAC if we have it.
- * This can happen because the remote MAC entries are also added as "dynamic",
- * so the kernel can ageout the entry.
+ * Handle network MAC delete by kernel -
+ * 1. readd the remote MAC if we have it
+ * 2. local MAC with does ES may also need to be re-installed
  */
-int zebra_vxlan_check_readd_remote_mac(struct interface *ifp,
-                                      struct interface *br_if,
-                                      struct ethaddr *macaddr, vlanid_t vid)
+static int zebra_vxlan_do_local_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac);
+int zebra_vxlan_dp_network_mac_del(struct interface *ifp,
+                                  struct interface *br_if,
+                                  struct ethaddr *macaddr, vlanid_t vid)
 {
        struct zebra_if *zif = NULL;
        struct zebra_l2info_vxlan *vxl = NULL;
@@ -3984,7 +4019,6 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp,
        zebra_evpn_t *zevpn = NULL;
        zebra_l3vni_t *zl3vni = NULL;
        zebra_mac_t *mac = NULL;
-       char buf[ETHER_ADDR_STRLEN];
 
        zif = ifp->info;
        assert(zif);
@@ -4010,16 +4044,89 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp,
        if (!mac)
                return 0;
 
-       /* Is it a remote entry? */
-       if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
-               return 0;
+       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+               /* If remote entry simply re-install */
+               if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+                       zlog_debug(
+                               "dpDel remote MAC %pEA intf %s(%u) VNI %u - readd",
+                               macaddr, ifp->name, ifp->ifindex, vni);
+               zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */);
+       } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) && mac->es
+                  && zebra_evpn_es_local_mac_via_network_port(mac->es)) {
+               /* If local entry via nw-port call local-del which will
+                * re-install entry in the dataplane is needed
+                */
+               if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+                       zlog_debug("dpDel local-nw-MAC %pEA VNI %u", macaddr,
+                                  vni);
+               zebra_vxlan_do_local_mac_del(zevpn, mac);
+       }
+
+       return 0;
+}
+
+static int zebra_vxlan_do_local_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac)
+{
+       bool old_bgp_ready;
+       bool new_bgp_ready;
 
        if (IS_ZEBRA_DEBUG_VXLAN)
-               zlog_debug("Del remote MAC %s intf %s(%u) VNI %u - readd",
-                          prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
-                          ifp->ifindex, vni);
+               zlog_debug("DEL MAC %pEA VNI %u seq %u flags 0x%x nbr count %u",
+                          &mac->macaddr, zevpn->vni, mac->loc_seq, mac->flags,
+                          listcount(mac->neigh_list));
+
+       old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags);
+       if (zebra_evpn_mac_is_static(mac)) {
+               /* this is a synced entry and can only be removed when the
+                * es-peers stop advertising it.
+                */
+               memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+
+               if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+                       zlog_debug(
+                               "re-add sync-mac vni %u mac %pEA es %s seq %d f 0x%x",
+                               zevpn->vni, &mac->macaddr,
+                               mac->es ? mac->es->esi_str : "-", mac->loc_seq,
+                               mac->flags);
+
+               /* inform-bgp about change in local-activity if any */
+               if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) {
+                       SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE);
+                       new_bgp_ready =
+                               zebra_evpn_mac_is_ready_for_bgp(mac->flags);
+                       zebra_evpn_mac_send_add_del_to_client(
+                               mac, old_bgp_ready, new_bgp_ready);
+               }
+
+               /* re-install the entry in the kernel */
+               zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */,
+                                              false /* force_clear_static */,
+                                              __func__);
+
+               return 0;
+       }
+
+       /* Update all the neigh entries associated with this mac */
+       zebra_evpn_process_neigh_on_local_mac_del(zevpn, mac);
+
+       /* Remove MAC from BGP. */
+       zebra_evpn_mac_send_del_to_client(zevpn->vni, &mac->macaddr, mac->flags,
+                                         false /* force */);
+
+       zebra_evpn_es_mac_deref_entry(mac);
+
+       /*
+        * If there are no neigh associated with the mac delete the mac
+        * else mark it as AUTO for forward reference
+        */
+       if (!listcount(mac->neigh_list)) {
+               zebra_evpn_mac_del(zevpn, mac);
+       } else {
+               UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS);
+               UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+               SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+       }
 
-       zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */);
        return 0;
 }
 
@@ -4030,6 +4137,7 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
                              struct ethaddr *macaddr, vlanid_t vid)
 {
        zebra_evpn_t *zevpn;
+       zebra_mac_t *mac;
 
        /* We are interested in MACs only on ports or (port, VLAN) that
         * map to a VNI.
@@ -4044,7 +4152,16 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
                return -1;
        }
 
-       return zebra_evpn_del_local_mac(zevpn, macaddr, ifp);
+       /* If entry doesn't exist, nothing to do. */
+       mac = zebra_evpn_mac_lookup(zevpn, macaddr);
+       if (!mac)
+               return 0;
+
+       /* Is it a local entry? */
+       if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
+               return 0;
+
+       return zebra_vxlan_do_local_mac_del(zevpn, mac);
 }
 
 /*
index 534e29936d5b6c05b4cedd6afe5e0e735b336e58..24de8ff04ee1f814de96979b20fcfdab8333e597 100644 (file)
@@ -177,13 +177,6 @@ extern int zebra_vxlan_local_mac_add_update(struct interface *ifp,
 extern int zebra_vxlan_local_mac_del(struct interface *ifp,
                                     struct interface *br_if,
                                     struct ethaddr *mac, vlanid_t vid);
-extern int zebra_vxlan_check_readd_remote_mac(struct interface *ifp,
-                                             struct interface *br_if,
-                                             struct ethaddr *mac,
-                                             vlanid_t vid);
-extern int zebra_vxlan_check_del_local_mac(struct interface *ifp,
-                                          struct interface *br_if,
-                                          struct ethaddr *mac, vlanid_t vid);
 extern int zebra_vxlan_check_readd_vtep(struct interface *ifp,
                                        struct in_addr vtep_ip);
 extern int zebra_vxlan_if_up(struct interface *ifp);
@@ -222,6 +215,15 @@ extern void zebra_evpn_init(void);
 extern void zebra_vxlan_macvlan_up(struct interface *ifp);
 extern void zebra_vxlan_macvlan_down(struct interface *ifp);
 extern int vni_list_cmp(void *p1, void *p2);
+extern int zebra_vxlan_dp_network_mac_add(struct interface *ifp,
+                                         struct interface *br_if,
+                                         struct ethaddr *macaddr, vlanid_t vid,
+                                         uint32_t nhg_id, bool sticky,
+                                         bool dp_static);
+extern int zebra_vxlan_dp_network_mac_del(struct interface *ifp,
+                                         struct interface *br_if,
+                                         struct ethaddr *macaddr,
+                                         vlanid_t vid);
 
 #ifdef __cplusplus
 }