From c0c7707d0d262d54b95a1e63bdb91b9a43ffc58f Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Tue, 3 Nov 2020 14:34:46 -0800 Subject: [PATCH 1/5] zebra: fix problem with SVI IP being advertised even if disabled SVI IP is being advertised unconditionally i.e. even if disabled (and that is the default config). This can be problematic when the SVI address is re-used across racks. Added the user config condition in all the relevant places where the SVI advertisement is triggered. Signed-off-by: Anuradha Karuppiah --- zebra/zebra_evpn.c | 21 ++++++++++++++++----- zebra/zebra_vxlan.c | 10 ++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index b232c664bc..2bab2997a3 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -151,6 +151,9 @@ void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt) buf, sizeof(buf))); json_object_string_add(json, "advertiseGatewayMacip", zevpn->advertise_gw_macip ? "Yes" : "No"); + json_object_string_add(json, "advertiseSviMacip", + zevpn->advertise_svi_macip ? "Yes" + : "No"); json_object_int_add(json, "numMacs", num_macs); json_object_int_add(json, "numArpNd", num_neigh); } @@ -194,6 +197,8 @@ void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt) num_neigh); vty_out(vty, " Advertise-gw-macip: %s\n", zevpn->advertise_gw_macip ? "Yes" : "No"); + vty_out(vty, " Advertise-svi-macip: %s\n", + zevpn->advertise_svi_macip ? "Yes" : "No"); } } @@ -569,7 +574,9 @@ void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket, return; /* Add primary SVI MAC-IP */ - zebra_evpn_add_macip_for_intf(vlan_if, zevpn); + if (advertise_svi_macip_enabled(zevpn) + || advertise_gw_macip_enabled(zevpn)) + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); if (advertise_gw_macip_enabled(zevpn)) { /* Add VRR MAC-IP - if any*/ @@ -927,12 +934,16 @@ void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp) if (vlan_if) { /* Add SVI MAC-IP */ - zebra_evpn_add_macip_for_intf(vlan_if, zevpn); + if (advertise_svi_macip_enabled(zevpn) + || advertise_gw_macip_enabled(zevpn)) + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); /* Add VRR MAC-IP - if any*/ - vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); - if (vrr_if) - zebra_evpn_add_macip_for_intf(vrr_if, zevpn); + if (advertise_gw_macip_enabled(zevpn)) { + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zebra_evpn_add_macip_for_intf(vrr_if, zevpn); + } neigh_read_for_vlan(zns, vlan_if); } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 424c00d5eb..b36a9612a7 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -4493,6 +4493,16 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, return -1; } + /* VRR IP is advertised only if gw-macip-adv-enabled */ + if (IS_ZEBRA_IF_MACVLAN(ifp)) { + if (!advertise_gw_macip_enabled(zevpn)) + return 0; + } else { + /* SVI IP is advertised if gw or svi macip-adv-enabled */ + if (!advertise_svi_macip_enabled(zevpn) + && !advertise_gw_macip_enabled(zevpn)) + return 0; + } memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); From 243b74eda653893f4ed7394fa5f7e1bb2ba2bee0 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Mon, 2 Nov 2020 10:53:50 -0800 Subject: [PATCH 2/5] zebra: changes to advertise SVI mac by default if evpn-mh is enabled Added support for advertising SVI MAC if EVPN-MH is enabled. In the case of EVPN MH arp replies from an attached server can be sent to the ES-peer. To prevent flooding of the reply the SVI MAC needs to be advertised by default. Note: advertise-svi-ip could have been used as an alternate way to advertise SVI MAC. However that config cannot be turned on if SVI IPs are re-used (which is done to avoid wasting IP addresses in a subnet). Signed-off-by: Anuradha Karuppiah --- zebra/if_netlink.c | 2 + zebra/interface.c | 5 ++ zebra/zebra_evpn.c | 2 + zebra/zebra_evpn_mac.c | 55 ++++++++++++++- zebra/zebra_evpn_mac.h | 10 +++ zebra/zebra_evpn_mh.c | 146 +++++++++++++++++++++++++++++++++++---- zebra/zebra_evpn_mh.h | 13 ++++ zebra/zebra_evpn_neigh.c | 2 +- zebra/zebra_vxlan.c | 9 +++ 9 files changed, 228 insertions(+), 16 deletions(-) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 00471b9645..3828f8800f 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -561,6 +561,8 @@ static void netlink_interface_update_l2info(struct interface *ifp, netlink_extract_vlan_info(link_data, &vlan_info); zebra_l2_vlanif_update(ifp, &vlan_info); + zebra_evpn_acc_bd_svi_set(ifp->info, NULL, + !!if_is_operative(ifp)); } else if (IS_ZEBRA_IF_VXLAN(ifp)) { struct zebra_l2info_vxlan vxlan_info; diff --git a/zebra/interface.c b/zebra/interface.c index fc34a6fb9e..f74030e4d8 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1183,6 +1183,11 @@ void zebra_if_update_all_links(void) zif->link?zif->link->name:"unk", zif->link_ifindex); } + + /* Update VLAN<=>SVI map */ + if (IS_ZEBRA_IF_VLAN(ifp)) + zebra_evpn_acc_bd_svi_set(zif, NULL, + !!if_is_operative(ifp)); } } diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 2bab2997a3..2047c711de 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -932,6 +932,8 @@ void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp) macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if); vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { + /* Add SVI MAC */ + zebra_evpn_acc_bd_svi_mac_add(vlan_if); /* Add SVI MAC-IP */ if (advertise_svi_macip_enabled(zevpn) diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index 5227a480fc..6d7c655dcc 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -221,8 +221,8 @@ void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevpn, zebra_mac_t *mac) UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); } - /* If no neighbors, delete the MAC. */ - if (list_isempty(mac->neigh_list)) + /* If no references, delete the MAC. */ + if (!zebra_evpn_mac_in_use(mac)) zebra_evpn_mac_del(zevpn, mac); } @@ -583,6 +583,9 @@ void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) json_object_boolean_true_add(json_mac, "stickyMac"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_SVI)) + json_object_boolean_true_add(json_mac, "sviMac"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) json_object_boolean_true_add(json_mac, "defaultGateway"); @@ -685,6 +688,9 @@ void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) vty_out(vty, " Sticky Mac "); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_SVI)) + vty_out(vty, " SVI-Mac "); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) vty_out(vty, " Default-gateway Mac "); @@ -2412,3 +2418,48 @@ int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, return 0; } + +void zebra_evpn_mac_svi_del(struct interface *ifp, zebra_evpn_t *zevpn) +{ + zebra_mac_t *mac; + struct ethaddr macaddr; + + if (!zebra_evpn_mh_do_adv_svi_mac()) + return; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + mac = zebra_evpn_mac_lookup(zevpn, &macaddr); + if (mac && CHECK_FLAG(mac->flags, ZEBRA_MAC_SVI)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("SVI %s mac free", ifp->name); + + UNSET_FLAG(mac->flags, ZEBRA_MAC_SVI); + zebra_evpn_deref_ip2mac(mac->zevpn, mac); + } +} + +void zebra_evpn_mac_svi_add(struct interface *ifp, zebra_evpn_t *zevpn) +{ + zebra_mac_t *mac = NULL; + struct ethaddr macaddr; + struct zebra_if *zif = ifp->info; + + if (!zebra_evpn_mh_do_adv_svi_mac()) + return; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + /* dup check */ + mac = zebra_evpn_mac_lookup(zevpn, &macaddr); + if (mac && CHECK_FLAG(mac->flags, ZEBRA_MAC_SVI)) + return; + + /* add/update mac */ + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("SVI %s mac add", zif->ifp->name); + + mac = NULL; + zebra_evpn_mac_gw_macip_add(ifp, zevpn, NULL, &mac, &macaddr, 0); + if (mac) + SET_FLAG(mac->flags, ZEBRA_MAC_SVI); +} diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h index 242097907f..fd6257faf0 100644 --- a/zebra/zebra_evpn_mac.h +++ b/zebra/zebra_evpn_mac.h @@ -80,6 +80,8 @@ struct zebra_mac_t_ { * to advertise it as locally attached but with a "proxy" flag */ #define ZEBRA_MAC_LOCAL_INACTIVE 0x800 +/* The MAC entry was created because of advertise_svi_mac */ +#define ZEBRA_MAC_SVI 0x1000 #define ZEBRA_MAC_ALL_LOCAL_FLAGS (ZEBRA_MAC_LOCAL | ZEBRA_MAC_LOCAL_INACTIVE) #define ZEBRA_MAC_ALL_PEER_FLAGS \ @@ -201,6 +203,12 @@ static inline void zebra_evpn_mac_clear_sync_info(zebra_mac_t *mac) zebra_evpn_mac_stop_hold_timer(mac); } +static inline bool zebra_evpn_mac_in_use(zebra_mac_t *mac) +{ + return !list_isempty(mac->neigh_list) + || CHECK_FLAG(mac->flags, ZEBRA_MAC_SVI); +} + struct hash *zebra_mac_db_create(const char *desc); uint32_t num_valid_macs(zebra_evpn_t *zevi); uint32_t num_dup_detected_macs(zebra_evpn_t *zevi); @@ -257,6 +265,8 @@ int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac); int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, struct ipaddr *ip, zebra_mac_t **macp, struct ethaddr *macaddr, vlanid_t vlan_id); +void zebra_evpn_mac_svi_add(struct interface *ifp, zebra_evpn_t *zevpn); +void zebra_evpn_mac_svi_del(struct interface *ifp, zebra_evpn_t *zevpn); #ifdef __cplusplus } diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 7e712bf1ee..f010ab7306 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -524,9 +524,11 @@ static struct zebra_evpn_access_bd *zebra_evpn_acc_vl_find(vlanid_t vid) /* A new broadcast domain can be created when a VLAN member or VLAN<=>VxLAN_IF * mapping is added. */ -static struct zebra_evpn_access_bd *zebra_evpn_acc_vl_new(vlanid_t vid) +static struct zebra_evpn_access_bd * +zebra_evpn_acc_vl_new(vlanid_t vid, struct interface *br_if) { struct zebra_evpn_access_bd *acc_bd; + struct interface *vlan_if; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("access vlan %d add", vid); @@ -544,6 +546,16 @@ static struct zebra_evpn_access_bd *zebra_evpn_acc_vl_new(vlanid_t vid) return NULL; } + /* check if an svi exists for the vlan */ + if (br_if) { + vlan_if = zvni_map_to_svi(vid, br_if); + if (vlan_if) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("vlan %d SVI %s set", vid, + vlan_if->name); + acc_bd->vlan_zif = vlan_if->info; + } + } return acc_bd; } @@ -556,6 +568,9 @@ static void zebra_evpn_acc_vl_free(struct zebra_evpn_access_bd *acc_bd) if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("access vlan %d del", acc_bd->vid); + if (acc_bd->vlan_zif && acc_bd->zevpn && acc_bd->zevpn->mac_table) + zebra_evpn_mac_svi_del(acc_bd->vlan_zif->ifp, acc_bd->zevpn); + /* cleanup resources maintained against the ES */ list_delete(&acc_bd->mbr_zifs); @@ -584,6 +599,59 @@ static void zebra_evpn_acc_bd_free_on_deref(struct zebra_evpn_access_bd *acc_bd) zebra_evpn_acc_vl_free(acc_bd); } +/* called when a SVI is goes up/down */ +void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, + struct zebra_if *br_zif, bool is_up) +{ + struct zebra_evpn_access_bd *acc_bd; + struct zebra_l2info_bridge *br; + uint16_t vid; + struct zebra_if *tmp_br_zif = br_zif; + + if (!tmp_br_zif) { + if (!vlan_zif->link) + return; + + tmp_br_zif = vlan_zif->link->info; + } + + br = &tmp_br_zif->l2info.br; + /* ignore vlan unaware bridges */ + if (!br->vlan_aware) + return; + + vid = vlan_zif->l2info.vl.vid; + acc_bd = zebra_evpn_acc_vl_find(vid); + if (!acc_bd) + return; + + if (is_up) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("vlan %d SVI %s set", vid, + vlan_zif->ifp->name); + + acc_bd->vlan_zif = vlan_zif; + if (acc_bd->zevpn) + zebra_evpn_mac_svi_add(acc_bd->vlan_zif->ifp, + acc_bd->zevpn); + } else if (acc_bd->vlan_zif) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("vlan %d SVI clear", vid); + acc_bd->vlan_zif = NULL; + if (acc_bd->zevpn && acc_bd->zevpn->mac_table) + zebra_evpn_mac_svi_del(vlan_zif->ifp, acc_bd->zevpn); + } +} + +/* On some events macs are force-flushed. This api can be used to reinstate + * the svi-mac after such cleanup-events. + */ +void zebra_evpn_acc_bd_svi_mac_add(struct interface *vlan_if) +{ + zebra_evpn_acc_bd_svi_set(vlan_if->info, NULL, + if_is_operative(vlan_if)); +} + /* called when a EVPN-L2VNI is set or cleared against a BD */ static void zebra_evpn_acc_bd_evpn_set(struct zebra_evpn_access_bd *acc_bd, zebra_evpn_t *zevpn, zebra_evpn_t *old_zevpn) @@ -604,6 +672,15 @@ static void zebra_evpn_acc_bd_evpn_set(struct zebra_evpn_access_bd *acc_bd, else if (old_zevpn) zebra_evpn_local_es_evi_del(zif->es_info.es, old_zevpn); } + + if (acc_bd->vlan_zif) { + if (zevpn) + zebra_evpn_mac_svi_add(acc_bd->vlan_zif->ifp, + acc_bd->zevpn); + else if (old_zevpn && old_zevpn->mac_table) + zebra_evpn_mac_svi_del(acc_bd->vlan_zif->ifp, + old_zevpn); + } } /* handle VLAN->VxLAN_IF association */ @@ -618,7 +695,8 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif) acc_bd = zebra_evpn_acc_vl_find(vid); if (!acc_bd) - acc_bd = zebra_evpn_acc_vl_new(vid); + acc_bd = zebra_evpn_acc_vl_new(vid, + vxlan_zif->brslave_info.br_if); old_vxlan_zif = acc_bd->vxlan_zif; acc_bd->vxlan_zif = vxlan_zif; @@ -712,7 +790,7 @@ void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif) acc_bd = zebra_evpn_acc_vl_find(vid); if (!acc_bd) - acc_bd = zebra_evpn_acc_vl_new(vid); + acc_bd = zebra_evpn_acc_vl_new(vid, zif->brslave_info.br_if); if (listnode_lookup(acc_bd->mbr_zifs, zif)) return; @@ -756,6 +834,22 @@ void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif) zebra_evpn_acc_bd_free_on_deref(acc_bd); } +static void zebra_evpn_acc_vl_adv_svi_mac_cb(struct hash_bucket *bucket, + void *ctxt) +{ + struct zebra_evpn_access_bd *acc_bd = bucket->data; + + if (acc_bd->vlan_zif && acc_bd->zevpn) + zebra_evpn_mac_svi_add(acc_bd->vlan_zif->ifp, acc_bd->zevpn); +} + +/* called when advertise SVI MAC is enabled on the switch */ +static void zebra_evpn_acc_vl_adv_svi_mac_all(void) +{ + hash_iterate(zmh_info->evpn_vlan_table, + zebra_evpn_acc_vl_adv_svi_mac_cb, NULL); +} + static void zebra_evpn_acc_vl_json_fill(struct zebra_evpn_access_bd *acc_bd, json_object *json, bool detail) { @@ -800,6 +894,8 @@ static void zebra_evpn_acc_vl_show_entry_detail(struct vty *vty, vty_out(vty, " VxLAN Interface: %s\n", acc_bd->vxlan_zif ? acc_bd->vxlan_zif->ifp->name : "-"); + vty_out(vty, " SVI: %s\n", + acc_bd->vlan_zif ? acc_bd->vlan_zif->ifp->name : "-"); vty_out(vty, " L2-VNI: %d\n", acc_bd->zevpn ? acc_bd->zevpn->vni : 0); vty_out(vty, " Member Count: %d\n", @@ -817,12 +913,11 @@ static void zebra_evpn_acc_vl_show_entry(struct vty *vty, if (json) { zebra_evpn_acc_vl_json_fill(acc_bd, json, false); } else { - vty_out(vty, "%-5u %21s %-8d %u\n", - acc_bd->vid, - acc_bd->vxlan_zif ? - acc_bd->vxlan_zif->ifp->name : "-", - acc_bd->zevpn ? acc_bd->zevpn->vni : 0, - listcount(acc_bd->mbr_zifs)); + vty_out(vty, "%-5u %-15s %-8d %-15s %u\n", acc_bd->vid, + acc_bd->vlan_zif ? acc_bd->vlan_zif->ifp->name : "-", + acc_bd->zevpn ? acc_bd->zevpn->vni : 0, + acc_bd->vxlan_zif ? acc_bd->vxlan_zif->ifp->name : "-", + listcount(acc_bd->mbr_zifs)); } } @@ -856,8 +951,8 @@ void zebra_evpn_acc_vl_show(struct vty *vty, bool uj) wctx.detail = false; if (!uj) - vty_out(vty, "%-5s %21s %-8s %s\n", - "VLAN", "VxLAN-IF", "L2-VNI", "# Members"); + vty_out(vty, "%-5s %-15s %-8s %-15s %s\n", "VLAN", "SVI", + "L2-VNI", "VXLAN-IF", "# Members"); hash_iterate(zmh_info->evpn_vlan_table, zebra_evpn_acc_vl_show_hash, &wctx); @@ -944,6 +1039,7 @@ void zebra_evpn_if_cleanup(struct zebra_if *zif) vlanid_t vid; struct zebra_evpn_es *es; + zebra_evpn_acc_bd_svi_set(zif, NULL, false); if (!bf_is_inited(zif->vlan_bitmap)) return; @@ -1960,6 +2056,20 @@ static void zebra_evpn_mh_advertise_reach_neigh_only(void) */ } +/* On config of first local-ES turn on advertisement of local SVI-MAC */ +static void zebra_evpn_mh_advertise_svi_mac(void) +{ + if (zmh_info->flags & ZEBRA_EVPN_MH_ADV_SVI_MAC) + return; + + zmh_info->flags |= ZEBRA_EVPN_MH_ADV_SVI_MAC; + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("evpn-mh: advertise SVI MAC"); + + /* walk through all SVIs and see if we need to advertise the MAC */ + zebra_evpn_acc_vl_adv_svi_mac_all(); +} + static int zebra_evpn_es_df_delay_exp_cb(struct thread *t) { struct zebra_evpn_es *es; @@ -1974,6 +2084,17 @@ static int zebra_evpn_es_df_delay_exp_cb(struct thread *t) return 0; } +/* currently there is no global config to turn on MH instead we use + * the addition of the first local Ethernet Segment as the trigger to + * init MH specific processing + */ +static void zebra_evpn_mh_on_first_local_es(void) +{ + zebra_evpn_mh_dup_addr_detect_off(); + zebra_evpn_mh_advertise_reach_neigh_only(); + zebra_evpn_mh_advertise_svi_mac(); +} + static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, struct zebra_if *zif) { @@ -1984,8 +2105,7 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, zlog_debug("local es %s add; nhg %u if %s", es->esi_str, es->nhg_id, zif->ifp->name); - zebra_evpn_mh_dup_addr_detect_off(); - zebra_evpn_mh_advertise_reach_neigh_only(); + zebra_evpn_mh_on_first_local_es(); es->flags |= ZEBRA_EVPNES_LOCAL; listnode_init(&es->local_es_listnode, es); diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 81ae740d49..6d21434778 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -180,6 +180,8 @@ struct zebra_evpn_access_bd { struct list *mbr_zifs; /* presence of zevpn activates the EVI on all the ESs in mbr_zifs */ zebra_evpn_t *zevpn; + /* SVI associated with the VLAN */ + struct zebra_if *vlan_zif; }; /* multihoming information stored in zrouter */ @@ -200,6 +202,10 @@ struct zebra_evpn_mh_info { * this flag when the first local ES is detected. */ #define ZEBRA_EVPN_MH_ADV_REACHABLE_NEIGH_ONLY (1 << 2) +/* If EVPN MH is enabled we advertise the SVI MAC address to avoid + * flooding of ARP replies rxed from the multi-homed host + */ +#define ZEBRA_EVPN_MH_ADV_SVI_MAC (1 << 3) /* RB tree of Ethernet segments (used for EVPN-MH) */ struct zebra_es_rb_head es_rb_tree; @@ -285,6 +291,10 @@ static inline bool zebra_evpn_mh_do_adv_reachable_neigh_only(void) return !!(zmh_info->flags & ZEBRA_EVPN_MH_ADV_REACHABLE_NEIGH_ONLY); } +static inline bool zebra_evpn_mh_do_adv_svi_mac(void) +{ + return zmh_info && (zmh_info->flags & ZEBRA_EVPN_MH_ADV_SVI_MAC); +} /*****************************************************************************/ extern esi_t *zero_esi; @@ -357,5 +367,8 @@ 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 void zebra_evpn_l2_nh_show(struct vty *vty, bool uj); +extern void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, + struct zebra_if *br_zif, bool is_up); +extern void zebra_evpn_acc_bd_svi_mac_add(struct interface *vlan_if); #endif /* _ZEBRA_EVPN_MH_H */ diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index 1f45b72e3a..834ad5381d 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -2464,7 +2464,7 @@ int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip) /* see if the AUTO mac needs to be deleted */ if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO) - && !listcount(zmac->neigh_list)) + && !zebra_evpn_mac_in_use(zmac)) zebra_evpn_mac_del(zevpn, zmac); return 0; diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index b36a9612a7..a4365e551f 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -3503,6 +3503,8 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) zvrf->advertise_gw_macip ? "Yes" : "No"); vty_out(vty, "Advertise svi mac-ip: %s\n", zvrf->advertise_svi_macip ? "Yes" : "No"); + vty_out(vty, "Advertise svi mac: %s\n", + zebra_evpn_mh_do_adv_svi_mac() ? "Yes" : "No"); vty_out(vty, "Duplicate address detection: %s\n", zebra_evpn_do_dup_addr_detect(zvrf) ? "Enable" : "Disable"); @@ -4549,10 +4551,14 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) } else { zebra_evpn_t *zevpn = NULL; + /* Unlink the SVI from the access VLAN */ + zebra_evpn_acc_bd_svi_set(ifp->info, link_if->info, false); + /* since we dont have svi corresponding to zevpn, we associate it * to default vrf. Note: the corresponding neigh entries on the * SVI would have already been deleted */ zevpn = zebra_evpn_from_svi(ifp, link_if); + if (zevpn) { zevpn->vrf_id = VRF_DEFAULT; @@ -4616,6 +4622,9 @@ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) n_wctx.zevpn = zevpn; hash_iterate(zevpn->neigh_table, zebra_evpn_install_neigh_hash, &n_wctx); + + /* Link the SVI from the access VLAN */ + zebra_evpn_acc_bd_svi_set(ifp->info, link_if->info, true); } return 0; From 3b63732a429aae946ce380ae6f57979bde731e71 Mon Sep 17 00:00:00 2001 From: Chirag Shah Date: Tue, 24 Nov 2020 12:22:49 -0800 Subject: [PATCH 3/5] zebra: prevent crash in evpn if cleanup zebra crash is seen while cleaning up evpn interface during shutdown event. evpn interface clean up is called from vrf_delete callback (gdb) frame 4 (is_up=false, br_zif=0x0, vlan_zif=0x557f31fb36f0) at zebra/zebra_evpn_mh.c:614 614 zebra/zebra_evpn_mh.c: No such file or directory. (gdb) p tmp_br_zif $1 = (struct zebra_if *) 0x0 (gdb) p vlan_zif->link $2 = (struct interface *) 0x557f31fb2d40 (gdb) p vlan_zif->link->info $3 = (void *) 0x0 (gdb) p zebra_if->ifp->name No symbol "zebra_if" in current context. (gdb) p vlan_zif->ifp->name $4 = "peerlink-3.4094\000\000\000\000" Ticket:CM-32435 Reviewed By:CCR-10957 Testing Done: Signed-off-by: Chirag Shah --- zebra/zebra_evpn_mh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index f010ab7306..0534e130b1 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -609,7 +609,7 @@ void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, struct zebra_if *tmp_br_zif = br_zif; if (!tmp_br_zif) { - if (!vlan_zif->link) + if (!vlan_zif->link || !vlan_zif->link->info) return; tmp_br_zif = vlan_zif->link->info; From bd2ac9a794cd124594fc92d247ba8851a23de1b4 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Thu, 21 Jan 2021 17:55:35 -0800 Subject: [PATCH 4/5] zebra: drop the SVI MAC cleanup done as a part of interface delete As a part of FRR shutdown interfaces are force flushed (in an arbitary order). Interfaces are already down at that point i.e. resources like SVI-MAC have already been released. Attempting to clean it up again as a part of the force-flush was resulting in access of freed up memory - >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ==26457== Thread 1: ==26457== Invalid read of size 8 ==26457== at 0x1AE6B0: zebra_evpn_acc_bd_svi_set (zebra_evpn_mh.c:606) ==26457== by 0x1B1460: zebra_evpn_if_cleanup (zebra_evpn_mh.c:1040) ==26457== by 0x13CA69: if_zebra_delete_hook (interface.c:244) ==26457== by 0x48A0E34: hook_call_if_del (if.c:59) ==26457== by 0x48A0E34: if_delete_retain (if.c:290) ==26457== by 0x48A2F94: if_delete (if.c:313) ==26457== by 0x48A3169: if_terminate (if.c:1217) ==26457== by 0x48E0024: vrf_delete (vrf.c:254) ==26457== by 0x48E0024: vrf_delete (vrf.c:225) ==26457== by 0x48E02FE: vrf_terminate (vrf.c:551) ==26457== by 0x1442E1: sigint (main.c:203) ==26457== by 0x1442E1: sigint (main.c:141) ==26457== by 0x48CF862: quagga_sigevent_process (sigevent.c:103) ==26457== by 0x48DD324: thread_fetch (thread.c:1404) ==26457== by 0x48A926A: frr_run (libfrr.c:1122) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> (gdb) bt (gdb) fr 5 1037 zebra/zebra_evpn_mh.c: No such file or directory. (gdb) p zif->ifp->name $2 = "vlan131", '\000' (gdb) p zif->link->info $5 = (void *) 0x1 (gdb) p/x zif->ifp->flags $7 = 0x1002 (gdb) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Ticket: CM-32435 Signed-off-by: Anuradha Karuppiah --- zebra/zebra_evpn_mh.c | 1 - 1 file changed, 1 deletion(-) diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 0534e130b1..bb109e9921 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -1039,7 +1039,6 @@ void zebra_evpn_if_cleanup(struct zebra_if *zif) vlanid_t vid; struct zebra_evpn_es *es; - zebra_evpn_acc_bd_svi_set(zif, NULL, false); if (!bf_is_inited(zif->vlan_bitmap)) return; From e4c3ece6e0bca4ee457a19211e5f51316dea7525 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Wed, 17 Feb 2021 18:36:56 -0800 Subject: [PATCH 5/5] zebra: fix problem with SVI MAC not being sent to BGP For MH the SVI MAC is advertised to prevent flooding of ARP replies. But because of a bug the SVI MAC was being added to the zebra database but not sent to bgpd for advertising. Ticket: CM-33329 Signed-off-by: Anuradha Karuppiah --- zebra/zebra_evpn.c | 2 +- zebra/zebra_evpn_mac.c | 25 +++++++++++++++++++++---- zebra/zebra_evpn_mac.h | 3 ++- zebra/zebra_evpn_mh.c | 23 +++++++++++++++++------ zebra/zebra_evpn_mh.h | 6 ++++++ 5 files changed, 47 insertions(+), 12 deletions(-) diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 2047c711de..d7076ccce6 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -434,7 +434,7 @@ int zebra_evpn_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, vxl = &zif->l2info.vxl; if (zebra_evpn_mac_gw_macip_add(ifp, zevpn, ip, &mac, macaddr, - vxl->access_vlan) + vxl->access_vlan, true) != 0) return -1; diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index 6d7c655dcc..90227aa597 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -2382,7 +2382,8 @@ int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac) int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, struct ipaddr *ip, zebra_mac_t **macp, - struct ethaddr *macaddr, vlanid_t vlan_id) + struct ethaddr *macaddr, vlanid_t vlan_id, + bool def_gw) { char buf[ETHER_ADDR_STRLEN]; zebra_mac_t *mac; @@ -2408,7 +2409,8 @@ int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, /* Set "local" forwarding info. */ SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); + if (def_gw) + SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); mac->fwd_info.local.ifindex = ifp->ifindex; mac->fwd_info.local.ns_id = local_ns_id; @@ -2423,6 +2425,7 @@ void zebra_evpn_mac_svi_del(struct interface *ifp, zebra_evpn_t *zevpn) { zebra_mac_t *mac; struct ethaddr macaddr; + bool old_bgp_ready; if (!zebra_evpn_mh_do_adv_svi_mac()) return; @@ -2433,7 +2436,10 @@ void zebra_evpn_mac_svi_del(struct interface *ifp, zebra_evpn_t *zevpn) if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("SVI %s mac free", ifp->name); + old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); UNSET_FLAG(mac->flags, ZEBRA_MAC_SVI); + zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready, + false); zebra_evpn_deref_ip2mac(mac->zevpn, mac); } } @@ -2443,8 +2449,11 @@ void zebra_evpn_mac_svi_add(struct interface *ifp, zebra_evpn_t *zevpn) zebra_mac_t *mac = NULL; struct ethaddr macaddr; struct zebra_if *zif = ifp->info; + bool old_bgp_ready; + bool new_bgp_ready; - if (!zebra_evpn_mh_do_adv_svi_mac()) + if (!zebra_evpn_mh_do_adv_svi_mac() + || !zebra_evpn_send_to_client_ok(zevpn)) return; memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); @@ -2458,8 +2467,16 @@ void zebra_evpn_mac_svi_add(struct interface *ifp, zebra_evpn_t *zevpn) if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("SVI %s mac add", zif->ifp->name); + old_bgp_ready = (mac && zebra_evpn_mac_is_ready_for_bgp(mac->flags)) + ? true + : false; + mac = NULL; - zebra_evpn_mac_gw_macip_add(ifp, zevpn, NULL, &mac, &macaddr, 0); + zebra_evpn_mac_gw_macip_add(ifp, zevpn, NULL, &mac, &macaddr, 0, false); if (mac) SET_FLAG(mac->flags, ZEBRA_MAC_SVI); + + 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); } diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h index fd6257faf0..c021765843 100644 --- a/zebra/zebra_evpn_mac.h +++ b/zebra/zebra_evpn_mac.h @@ -264,7 +264,8 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac); int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, struct ipaddr *ip, zebra_mac_t **macp, - struct ethaddr *macaddr, vlanid_t vlan_id); + struct ethaddr *macaddr, vlanid_t vlan_id, + bool def_gw); void zebra_evpn_mac_svi_add(struct interface *ifp, zebra_evpn_t *zevpn); void zebra_evpn_mac_svi_del(struct interface *ifp, zebra_evpn_t *zevpn); diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index bb109e9921..0bb1ebb564 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -130,12 +130,6 @@ static struct zebra_evpn_es_evi *zebra_evpn_es_evi_new(struct zebra_evpn_es *es, return es_evi; } -/* returns TRUE if the EVPN is ready to be sent to BGP */ -static inline bool zebra_evpn_send_to_client_ok(zebra_evpn_t *zevpn) -{ - return !!(zevpn->flags & ZEVPN_READY_FOR_BGP); -} - /* Evaluate if the es_evi is ready to be sent BGP - * 1. If it is ready an add is sent to BGP * 2. If it is not ready a del is sent (if the ES had been previously added @@ -466,6 +460,9 @@ void zebra_evpn_update_all_es(zebra_evpn_t *zevpn) { struct zebra_evpn_es_evi *es_evi; struct listnode *node; + struct interface *vlan_if; + struct interface *vxlan_if; + struct zebra_if *vxlan_zif; /* the EVPN is now elgible as a base for EVPN-MH */ if (zebra_evpn_send_to_client_ok(zevpn)) @@ -475,6 +472,20 @@ void zebra_evpn_update_all_es(zebra_evpn_t *zevpn) for (ALL_LIST_ELEMENTS_RO(zevpn->local_es_evi_list, node, es_evi)) zebra_evpn_es_evi_re_eval_send_to_client(es_evi); + + /* reinstall SVI MAC */ + vxlan_if = zevpn->vxlan_if; + if (vxlan_if) { + vxlan_zif = vxlan_if->info; + if (if_is_operative(vxlan_if) + && vxlan_zif->brslave_info.br_if) { + vlan_if = zvni_map_to_svi( + vxlan_zif->l2info.vxl.access_vlan, + vxlan_zif->brslave_info.br_if); + if (vlan_if) + zebra_evpn_acc_bd_svi_mac_add(vlan_if); + } + } } /*****************************************************************************/ diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 6d21434778..94de84ff14 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -262,6 +262,12 @@ struct zebra_evpn_mh_info { enum protodown_reasons protodown_rc; }; +/* returns TRUE if the EVPN is ready to be sent to BGP */ +static inline bool zebra_evpn_send_to_client_ok(zebra_evpn_t *zevpn) +{ + return !!(zevpn->flags & ZEVPN_READY_FOR_BGP); +} + static inline bool zebra_evpn_mac_is_es_local(zebra_mac_t *mac) { return mac->es && (mac->es->flags & ZEBRA_EVPNES_LOCAL);