diff options
Diffstat (limited to 'zebra')
| -rw-r--r-- | zebra/if_netlink.c | 31 | ||||
| -rw-r--r-- | zebra/interface.c | 10 | ||||
| -rw-r--r-- | zebra/interface.h | 14 | ||||
| -rw-r--r-- | zebra/irdp_interface.c | 18 | ||||
| -rw-r--r-- | zebra/rt_netlink.c | 4 | ||||
| -rw-r--r-- | zebra/zebra_dplane.c | 18 | ||||
| -rw-r--r-- | zebra/zebra_dplane.h | 5 | ||||
| -rw-r--r-- | zebra/zebra_evpn.c | 25 | ||||
| -rw-r--r-- | zebra/zebra_evpn_mac.c | 240 | ||||
| -rw-r--r-- | zebra/zebra_evpn_mac.h | 34 | ||||
| -rw-r--r-- | zebra/zebra_evpn_mh.c | 424 | ||||
| -rw-r--r-- | zebra/zebra_evpn_mh.h | 25 | ||||
| -rw-r--r-- | zebra/zebra_evpn_neigh.c | 10 | ||||
| -rw-r--r-- | zebra/zebra_l2.c | 64 | ||||
| -rw-r--r-- | zebra/zebra_l2.h | 2 | ||||
| -rw-r--r-- | zebra/zebra_vxlan.c | 36 |
16 files changed, 837 insertions, 123 deletions
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 00471b9645..98bde4b3c0 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; @@ -717,6 +719,21 @@ static void netlink_proc_dplane_if_protodown(struct zebra_if *zif, } } +static uint8_t netlink_parse_lacp_bypass(struct rtattr **linkinfo) +{ + uint8_t bypass = 0; + struct rtattr *mbrinfo[IFLA_BOND_SLAVE_MAX + 1]; + + memset(mbrinfo, 0, sizeof(mbrinfo)); + parse_rtattr_nested(mbrinfo, IFLA_BOND_SLAVE_MAX, + linkinfo[IFLA_INFO_SLAVE_DATA]); + if (mbrinfo[IFLA_BOND_SLAVE_AD_RX_BYPASS]) + bypass = *(uint8_t *)RTA_DATA( + mbrinfo[IFLA_BOND_SLAVE_AD_RX_BYPASS]); + + return bypass; +} + /* * Called from interface_lookup_netlink(). This function is only used * during bootstrap. @@ -741,6 +758,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) ifindex_t bond_ifindex = IFINDEX_INTERNAL; struct zebra_if *zif; ns_id_t link_nsid = ns_id; + uint8_t bypass = 0; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); @@ -814,6 +832,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) } else if (slave_kind && (strcmp(slave_kind, "bond") == 0)) { zif_slave_type = ZEBRA_IF_SLAVE_BOND; bond_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_MASTER]); + bypass = netlink_parse_lacp_bypass(linkinfo); } else zif_slave_type = ZEBRA_IF_SLAVE_OTHER; } @@ -880,7 +899,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) - zebra_l2if_update_bond_slave(ifp, bond_ifindex); + zebra_l2if_update_bond_slave(ifp, bond_ifindex, !!bypass); if (tb[IFLA_PROTO_DOWN]) { uint8_t protodown; @@ -1320,6 +1339,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) struct zebra_if *zif; ns_id_t link_nsid = ns_id; ifindex_t master_infindex = IFINDEX_INTERNAL; + uint8_t bypass = 0; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); @@ -1421,6 +1441,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) zif_slave_type = ZEBRA_IF_SLAVE_BOND; master_infindex = bond_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_MASTER]); + bypass = netlink_parse_lacp_bypass(linkinfo); } else zif_slave_type = ZEBRA_IF_SLAVE_OTHER; } @@ -1482,7 +1503,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) bridge_ifindex, ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) - zebra_l2if_update_bond_slave(ifp, bond_ifindex); + zebra_l2if_update_bond_slave(ifp, bond_ifindex, + !!bypass); if (tb[IFLA_PROTO_DOWN]) { uint8_t protodown; @@ -1595,7 +1617,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) bridge_ifindex, ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave) - zebra_l2if_update_bond_slave(ifp, bond_ifindex); + zebra_l2if_update_bond_slave(ifp, bond_ifindex, + !!bypass); if (tb[IFLA_PROTO_DOWN]) { uint8_t protodown; @@ -1631,6 +1654,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (IS_ZEBRA_IF_BOND(ifp)) zebra_l2if_update_bond(ifp, false); + if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) + zebra_l2if_update_bond_slave(ifp, bond_ifindex, false); /* Special handling for bridge or VxLAN interfaces. */ if (IS_ZEBRA_IF_BRIDGE(ifp)) zebra_l2_bridge_del(ifp); diff --git a/zebra/interface.c b/zebra/interface.c index fc34a6fb9e..6373b4b200 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -241,6 +241,7 @@ static int if_zebra_delete_hook(struct interface *ifp) #endif /* HAVE_RTADV */ zebra_evpn_if_cleanup(zebra_if); + zebra_evpn_mac_ifp_del(ifp); if_nhg_dependents_release(ifp); zebra_if_nhg_dependents_free(zebra_if); @@ -826,6 +827,7 @@ void if_delete_update(struct interface *ifp) memset(&zif->brslave_info, 0, sizeof(struct zebra_l2info_brslave)); zebra_evpn_if_cleanup(zif); + zebra_evpn_mac_ifp_del(ifp); } if (!ifp->configured) { @@ -1183,6 +1185,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)); } } @@ -1599,6 +1606,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) } } + if (zebra_if->flags & ZIF_FLAG_LACP_BYPASS) + vty_out(vty, " LACP bypass: on\n"); + zebra_evpn_if_es_print(vty, zebra_if); vty_out(vty, " protodown: %s %s\n", (zebra_if->flags & ZIF_FLAG_PROTODOWN) ? "on" : "off", diff --git a/zebra/interface.h b/zebra/interface.h index 8dcb477f10..64569742b4 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -286,6 +286,9 @@ struct zebra_es_if_info { esi_t esi; uint16_t df_pref; + uint8_t flags; +#define ZIF_CFG_ES_FLAG_BYPASS (1 << 0) + struct zebra_evpn_es *es; /* local ES */ }; @@ -297,7 +300,13 @@ enum zebra_if_flags { ZIF_FLAG_EVPN_MH_UPLINK_OPER_UP = (1 << 1), /* Dataplane protodown-on */ - ZIF_FLAG_PROTODOWN = (1 << 2) + ZIF_FLAG_PROTODOWN = (1 << 2), + + /* LACP bypass state is set by the dataplane on a bond member + * and inherited by the bond (if one or more bond members are in + * a bypass state the bond is placed in a bypass state) + */ + ZIF_FLAG_LACP_BYPASS = (1 << 3) }; /* `zebra' daemon local interface structure. */ @@ -386,6 +395,9 @@ struct zebra_if { */ enum protodown_reasons protodown_rc; + /* list of zebra_mac entries using this interface as destination */ + struct list *mac_list; + /* Link fields - for sub-interfaces. */ ifindex_t link_ifindex; struct interface *link; diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c index 2ab5fd3a4c..5352c6214d 100644 --- a/zebra/irdp_interface.c +++ b/zebra/irdp_interface.c @@ -93,10 +93,10 @@ static int irdp_if_delete(struct interface *ifp) return 0; } -static const char *inet_2a(uint32_t a, char *b) +static const char *inet_2a(uint32_t a, char *b, size_t b_len) { - sprintf(b, "%u.%u.%u.%u", (a)&0xFF, (a >> 8) & 0xFF, (a >> 16) & 0xFF, - (a >> 24) & 0xFF); + snprintf(b, b_len, "%u.%u.%u.%u", (a)&0xFF, (a >> 8) & 0xFF, + (a >> 16) & 0xFF, (a >> 24) & 0xFF); return b; } @@ -140,7 +140,8 @@ static int if_group(struct interface *ifp, int sock, uint32_t group, flog_err_sys(EC_LIB_SOCKET, "IRDP: %s can't setsockopt %s: %s", add_leave == IP_ADD_MEMBERSHIP ? "join group" : "leave group", - inet_2a(group, b1), safe_strerror(errno)); + inet_2a(group, b1, sizeof(b1)), + safe_strerror(errno)); return ret; } @@ -162,7 +163,8 @@ static int if_add_group(struct interface *ifp) if (irdp->flags & IF_DEBUG_MISC) zlog_debug("IRDP: Adding group %s for %s", - inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1), ifp->name); + inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1, sizeof(b1)), + ifp->name); return 0; } @@ -183,7 +185,8 @@ static int if_drop_group(struct interface *ifp) if (irdp->flags & IF_DEBUG_MISC) zlog_debug("IRDP: Leaving group %s for %s", - inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1), ifp->name); + inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1, sizeof(b1)), + ifp->name); return 0; } @@ -383,7 +386,8 @@ int irdp_config_write(struct vty *vty, struct interface *ifp) for (ALL_LIST_ELEMENTS_RO(irdp->AdvPrefList, node, adv)) vty_out(vty, " ip irdp address %s preference %d\n", - inet_2a(adv->ip.s_addr, b1), adv->pref); + inet_2a(adv->ip.s_addr, b1, sizeof(b1)), + adv->pref); vty_out(vty, " ip irdp holdtime %d\n", irdp->Lifetime); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index b980306149..99cb82e224 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1132,8 +1132,8 @@ static int build_label_stack(struct mpls_label_stack *nh_label, if (IS_ZEBRA_DEBUG_KERNEL) { if (!num_labels) - sprintf(label_buf, "label %u", - nh_label->label[i]); + snprintf(label_buf, label_buf_size, "label %u", + nh_label->label[i]); else { snprintf(label_buf1, sizeof(label_buf1), "/%u", nh_label->label[i]); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index db2b9e002e..b9b6b4af71 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -3251,6 +3251,24 @@ enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp, } /* + * Enqueue local mac del + */ +enum zebra_dplane_result +dplane_local_mac_del(const struct interface *ifp, + const struct interface *bridge_ifp, vlanid_t vid, + const struct ethaddr *mac) +{ + enum zebra_dplane_result result; + struct in_addr vtep_ip; + + vtep_ip.s_addr = 0; + + /* Use common helper api */ + result = mac_update_common(DPLANE_OP_MAC_DELETE, ifp, bridge_ifp, vid, + mac, vtep_ip, false, 0, 0); + return result; +} +/* * Public api to init an empty context - either newly-allocated or * reset/cleared - for a MAC update. */ diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 595d3fe562..f3ab1058a5 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -582,6 +582,11 @@ enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp, uint32_t set_static, uint32_t set_inactive); +enum zebra_dplane_result +dplane_local_mac_del(const struct interface *ifp, + const struct interface *bridge_ifp, vlanid_t vid, + const struct ethaddr *mac); + enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp, const struct interface *bridge_ifp, vlanid_t vid, diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index b232c664bc..d7076ccce6 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"); } } @@ -429,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; @@ -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*/ @@ -925,14 +932,20 @@ 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 */ - 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_evpn_mac.c b/zebra/zebra_evpn_mac.c index 5227a480fc..142a199bd7 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -94,6 +94,97 @@ uint32_t num_dup_detected_macs(zebra_evpn_t *zevpn) return num_macs; } +/* Setup mac_list against the access port. This is done when a mac uses + * the ifp as destination for the first time + */ +static void zebra_evpn_mac_ifp_new(struct zebra_if *zif) +{ + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("MAC list created for ifp %s (%u)", zif->ifp->name, + zif->ifp->ifindex); + + zif->mac_list = list_new(); + listset_app_node_mem(zif->mac_list); +} + +/* Free up the mac_list if any as a part of the interface del/cleanup */ +void zebra_evpn_mac_ifp_del(struct interface *ifp) +{ + struct zebra_if *zif = ifp->info; + + if (zif->mac_list) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("MAC list deleted for ifp %s (%u)", + zif->ifp->name, zif->ifp->ifindex); + list_delete(&zif->mac_list); + } +} + +/* Unlink local mac from a destination access port */ +static void zebra_evpn_mac_ifp_unlink(zebra_mac_t *zmac) +{ + struct zebra_if *zif; + struct interface *ifp = zmac->ifp; + + if (!ifp) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("VNI %d MAC %pEA unlinked from ifp %s (%u)", + zmac->zevpn->vni, + &zmac->macaddr, + ifp->name, ifp->ifindex); + + zif = ifp->info; + list_delete_node(zif->mac_list, &zmac->ifp_listnode); + zmac->ifp = NULL; +} + +/* Link local mac to destination access port. This is done only if the + * local mac is associated with a zero ESI i.e. single attach or lacp-bypass + * bridge port member + */ +static void zebra_evpn_mac_ifp_link(zebra_mac_t *zmac, struct interface *ifp) +{ + struct zebra_if *zif; + + if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) + return; + + /* already linked to the destination */ + if (zmac->ifp == ifp) + return; + + /* unlink the mac from any old destination */ + if (zmac->ifp) + zebra_evpn_mac_ifp_unlink(zmac); + + if (!ifp) + return; + + zif = ifp->info; + /* the interface mac_list is created on first mac link attempt */ + if (!zif->mac_list) + zebra_evpn_mac_ifp_new(zif); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("VNI %d MAC %pEA linked to ifp %s (%u)", + zmac->zevpn->vni, + &zmac->macaddr, + ifp->name, ifp->ifindex); + + zmac->ifp = ifp; + listnode_init(&zmac->ifp_listnode, zmac); + listnode_add(zif->mac_list, &zmac->ifp_listnode); +} + +/* If the mac is a local mac clear links to destination access port */ +void zebra_evpn_mac_clear_fwd_info(zebra_mac_t *zmac) +{ + zebra_evpn_mac_ifp_unlink(zmac); + memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info)); +} + /* * Install remote MAC into the forwarding plane. */ @@ -221,8 +312,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 +674,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 +779,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 "); @@ -747,14 +844,10 @@ static char *zebra_evpn_print_mac_flags(zebra_mac_t *mac, char *flags_buf, size_t flags_buf_sz) { snprintf(flags_buf, flags_buf_sz, "%s%s%s%s", - mac->sync_neigh_cnt ? - "N" : "", - (mac->flags & ZEBRA_MAC_ES_PEER_ACTIVE) ? - "P" : "", - (mac->flags & ZEBRA_MAC_ES_PEER_PROXY) ? - "X" : "", - (mac->flags & ZEBRA_MAC_LOCAL_INACTIVE) ? - "I" : ""); + mac->sync_neigh_cnt ? "N" : "", + (mac->flags & ZEBRA_MAC_ES_PEER_ACTIVE) ? "P" : "", + (mac->flags & ZEBRA_MAC_ES_PEER_PROXY) ? "X" : "", + (mac->flags & ZEBRA_MAC_LOCAL_INACTIVE) ? "I" : ""); return flags_buf; } @@ -1075,6 +1168,9 @@ int zebra_evpn_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac) /* force de-ref any ES entry linked to the MAC */ zebra_evpn_es_mac_deref_entry(mac); + /* remove links to the destination access port */ + zebra_evpn_mac_clear_fwd_info(mac); + /* Cancel proxy hold timer */ zebra_evpn_mac_stop_hold_timer(mac); @@ -1684,7 +1780,7 @@ zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevpn, struct ethaddr *macaddr, } } mac->rem_seq = 0; - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + zebra_evpn_mac_clear_fwd_info(mac); mac->flags = new_flags; if (IS_ZEBRA_DEBUG_EVPN_MH_MAC && (old_flags != new_flags)) { @@ -1710,6 +1806,7 @@ zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevpn, struct ethaddr *macaddr, inform_dataplane = true; ctx->mac_inactive = true; } + /* if peer-flag is being set notify dataplane that the * entry must not be expired because of local inactivity */ @@ -1790,20 +1887,25 @@ static bool zebra_evpn_local_mac_update_fwd_info(zebra_mac_t *mac, bool es_change; ns_id_t local_ns_id = NS_DEFAULT; struct zebra_vrf *zvrf; + struct zebra_evpn_es *es; zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); if (zvrf && zvrf->zns) local_ns_id = zvrf->zns->ns_id; - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + zebra_evpn_mac_clear_fwd_info(mac); - es_change = zebra_evpn_es_mac_ref_entry(mac, zif->es_info.es); + es = zif->es_info.es; + if (es && (es->flags & ZEBRA_EVPNES_BYPASS)) + es = NULL; + es_change = zebra_evpn_es_mac_ref_entry(mac, es); if (!mac->es) { /* if es is set fwd_info is not-relevant/taped-out */ mac->fwd_info.local.ifindex = ifp->ifindex; mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vid; + zebra_evpn_mac_ifp_link(mac, ifp); } return es_change; @@ -1908,6 +2010,8 @@ int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, esi_t *old_esi; bool old_static = false; zebra_mac_t *mac; + bool old_es_present; + bool new_es_present; sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); @@ -1971,7 +2075,16 @@ int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, zevpn, mac, seq, ipa_len, ipaddr, false)) return -1; + old_es_present = !!mac->es; zebra_evpn_es_mac_ref(mac, esi); + new_es_present = !!mac->es; + /* XXX - dataplane is curently not able to handle a MAC + * replace if the destination changes from L2-NHG to + * single VTEP and vice-versa. So delete the old entry + * and re-install + */ + if (old_es_present != new_es_present) + zebra_evpn_rem_mac_uninstall(zevpn, mac, false); } /* Check MAC's curent state is local (this is the case @@ -2016,8 +2129,8 @@ int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, } /* Set "auto" and "remote" forwarding info. */ + zebra_evpn_mac_clear_fwd_info(mac); UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); mac->fwd_info.r_vtep_ip = vtep_ip; @@ -2058,9 +2171,8 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, struct interface *ifp, struct ethaddr *macaddr, vlanid_t vid, bool sticky, bool local_inactive, - bool dp_static) + bool dp_static, zebra_mac_t *mac) { - zebra_mac_t *mac; char buf[ETHER_ADDR_STRLEN]; bool mac_sticky = false; bool inform_client = false; @@ -2077,7 +2189,8 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, assert(ifp); /* Check if we need to create or update or it is a NO-OP. */ - mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) zlog_debug( @@ -2128,6 +2241,8 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, old_static = zebra_evpn_mac_is_static(mac); if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) mac_sticky = true; + es_change = zebra_evpn_local_mac_update_fwd_info( + mac, ifp, vid); /* * Update any changes and if changes are relevant to @@ -2136,7 +2251,7 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, if (mac_sticky == sticky && old_ifp == ifp && old_vid == vid && old_local_inactive == local_inactive - && dp_static == old_static) { + && dp_static == old_static && !es_change) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( " Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u%s, " @@ -2160,15 +2275,17 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, inform_client = true; } - es_change = zebra_evpn_local_mac_update_fwd_info( - mac, ifp, vid); /* If an es_change is detected we need to advertise * the route with a sequence that is one * greater. This is need to indicate a mac-move * to the ES peers */ if (es_change) { - mac->loc_seq = mac->loc_seq + 1; + /* update the sequence number only if the entry + * is locally active + */ + if (!local_inactive) + mac->loc_seq = mac->loc_seq + 1; /* force drop the peer/sync info as it is * simply no longer relevant */ @@ -2302,7 +2419,8 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, return 0; } -int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac) +int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac, + bool clear_static) { char buf[ETHER_ADDR_STRLEN]; bool old_bgp_ready; @@ -2315,11 +2433,11 @@ int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac) listcount(mac->neigh_list)); old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); - if (zebra_evpn_mac_is_static(mac)) { + if (!clear_static && 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)); + zebra_evpn_mac_clear_fwd_info(mac); if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) { char mac_buf[MAC_BUF_SIZE]; @@ -2350,6 +2468,9 @@ int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac) return 0; } + /* flush the peer info */ + zebra_evpn_mac_clear_sync_info(mac); + /* Update all the neigh entries associated with this mac */ zebra_evpn_process_neigh_on_local_mac_del(zevpn, mac); @@ -2359,6 +2480,9 @@ int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac) zebra_evpn_es_mac_deref_entry(mac); + /* remove links to the destination access port */ + zebra_evpn_mac_clear_fwd_info(mac); + /* * If there are no neigh associated with the mac delete the mac * else mark it as AUTO for forward reference @@ -2376,7 +2500,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; @@ -2400,10 +2525,11 @@ int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, } /* Set "local" forwarding info. */ + zebra_evpn_mac_clear_fwd_info(mac); SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + if (def_gw) + SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); mac->fwd_info.local.ifindex = ifp->ifindex; mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vlan_id; @@ -2412,3 +2538,63 @@ 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; + bool old_bgp_ready; + + 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); + + 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); + } +} + +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() + || !zebra_evpn_send_to_client_ok(zevpn)) + 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); + + 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, 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 242097907f..fb162f1a93 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 \ @@ -88,7 +90,9 @@ struct zebra_mac_t_ { /* back pointer to zevpn */ zebra_evpn_t *zevpn; - /* Local or remote info. */ + /* Local or remote info. + * Note: fwd_info is only relevant if mac->es is NULL. + */ union { struct { ifindex_t ifindex; @@ -104,6 +108,16 @@ struct zebra_mac_t_ { /* memory used to link the mac to the es */ struct listnode es_listnode; + /* access-port/bridge member. only relevant for local macs that + * are associated with a zero-ESI, + * XXX - this belongs in fwd_info.local; however fwd_info is + * being cleared and memset to zero in different ways that can + * mess up the links. + */ + struct interface *ifp; + /* memory used to link the mac to the ifp */ + struct listnode ifp_listnode; + /* Mobility sequence numbers associated with this entry. */ uint32_t rem_seq; uint32_t loc_seq; @@ -201,6 +215,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); @@ -252,11 +272,17 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, struct interface *ifp, struct ethaddr *macaddr, vlanid_t vid, bool sticky, bool local_inactive, - bool dp_static); -int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac); + bool dp_static, zebra_mac_t *mac); +int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac, + bool clear_static); 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); +void zebra_evpn_mac_ifp_del(struct interface *ifp); +void zebra_evpn_mac_clear_fwd_info(zebra_mac_t *zmac); #ifdef __cplusplus } diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 7e712bf1ee..5a28ee10c6 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); + } + } } /*****************************************************************************/ @@ -524,9 +535,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 +557,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 +579,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 +610,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 || !vlan_zif->link->info) + 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 +683,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 +706,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 +801,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 +845,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 +905,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 +924,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 +962,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); @@ -1442,20 +1548,30 @@ static bool zebra_evpn_es_br_port_dplane_update(struct zebra_evpn_es *es, return false; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("es %s br-port dplane update by %s", es->esi_str, caller); + 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); + if (es->flags & ZEBRA_EVPNES_BYPASS) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug( + "es %s SPH filter disabled as it is in bypass", + es->esi_str); } 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; + 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; + } } } @@ -1503,6 +1619,7 @@ static bool 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) + || (es->flags & ZEBRA_EVPNES_BYPASS) || !zmh_info->es_originator_ip.s_addr) return zebra_evpn_es_df_change(es, new_non_df, caller, "not-ready"); @@ -1728,6 +1845,7 @@ static int zebra_evpn_es_send_add_to_client(struct zebra_evpn_es *es) struct zserv *client; struct stream *s; uint8_t oper_up; + bool bypass; client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); /* BGP may not be running. */ @@ -1742,15 +1860,18 @@ static int zebra_evpn_es_send_add_to_client(struct zebra_evpn_es *es) oper_up = !!(es->flags & ZEBRA_EVPNES_OPER_UP); stream_putc(s, oper_up); stream_putw(s, es->df_pref); + bypass = !!(es->flags & ZEBRA_EVPNES_BYPASS); + stream_putc(s, bypass); /* 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 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)); + zlog_debug( + "send add local es %s %pI4 active %u df_pref %u%s to %s", + es->esi_str, &zmh_info->es_originator_ip, oper_up, + es->df_pref, bypass ? " bypass" : "", + zebra_route_string(client->proto)); client->local_es_add_cnt++; return zserv_send_message(client, s); @@ -1874,18 +1995,50 @@ static void zebra_evpn_es_setup_evis(struct zebra_evpn_es *es) } } -static void zebra_evpn_es_local_mac_update(struct zebra_evpn_es *es, - bool force_clear_static) +static void zebra_evpn_flush_local_mac(zebra_mac_t *mac, struct interface *ifp) +{ + struct zebra_if *zif; + struct interface *br_ifp; + vlanid_t vid; + + zif = ifp->info; + br_ifp = zif->brslave_info.br_if; + if (!br_ifp) + return; + + if (mac->zevpn->vxlan_if) { + zif = mac->zevpn->vxlan_if->info; + vid = zif->l2info.vxl.access_vlan; + } else { + vid = 0; + } + + /* delete the local mac from the dataplane */ + dplane_local_mac_del(ifp, br_ifp, vid, &mac->macaddr); + /* delete the local mac in zebra */ + zebra_evpn_del_local_mac(mac->zevpn, mac, true); +} + +static void zebra_evpn_es_flush_local_macs(struct zebra_evpn_es *es, + struct interface *ifp, bool add) { zebra_mac_t *mac; struct listnode *node; + struct listnode *nnode; - for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) { - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) { - zebra_evpn_sync_mac_dp_install( - mac, false /* set_inactive */, - force_clear_static, __func__); - } + for (ALL_LIST_ELEMENTS(es->mac_list, node, nnode, mac)) { + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + continue; + + /* If ES is being attached/detached from the access port we + * need to clear local activity and peer activity and start + * over */ + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("VNI %u mac %pEA update; local ES %s %s", + mac->zevpn->vni, + &mac->macaddr, + es->esi_str, add ? "add" : "del"); + zebra_evpn_flush_local_mac(mac, ifp); } } @@ -1960,6 +2113,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 +2141,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 +2162,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); @@ -2004,6 +2181,10 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, if (zif->brslave_info.bridge_ifindex != IFINDEX_INTERNAL) es->flags |= ZEBRA_EVPNES_BR_PORT; + /* inherit the bypass flag from the interface */ + if (zif->flags & ZIF_FLAG_LACP_BYPASS) + es->flags |= ZEBRA_EVPNES_BYPASS; + /* setup base-vni if one doesn't already exist; the ES will get sent * to BGP as a part of that process */ @@ -2035,11 +2216,9 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, */ zebra_evpn_es_setup_evis(es); /* if there any local macs referring to the ES as dest we - * need to set the static reference on them if the MAC is - * synced from an ES peer + * need to clear the contents and start over */ - zebra_evpn_es_local_mac_update(es, - false /* force_clear_static */); + zebra_evpn_es_flush_local_macs(es, zif->ifp, true); /* inherit EVPN protodown flags on the access port */ zebra_evpn_mh_update_protodown_es(es, true /*resync_dplane*/); @@ -2054,33 +2233,34 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp) if (!(es->flags & ZEBRA_EVPNES_LOCAL)) return; + zif = es->zif; + + /* if there any local macs referring to the ES as dest we + * need to clear the contents and start over + */ + zebra_evpn_es_flush_local_macs(es, zif->ifp, false); + es->flags &= ~(ZEBRA_EVPNES_LOCAL | ZEBRA_EVPNES_READY_FOR_BGP); THREAD_OFF(es->df_delay_timer); - /* remove the DF filter */ - dplane_updated = zebra_evpn_es_run_df_election(es, __func__); - /* clear EVPN protodown flags on the access port */ zebra_evpn_mh_clear_protodown_es(es); - /* if there any local macs referring to the ES as dest we - * need to clear the static reference on them - */ - zebra_evpn_es_local_mac_update(es, - true /* force_clear_static */); + /* remove the DF filter */ + dplane_updated = zebra_evpn_es_run_df_election(es, __func__); /* 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; es->zif = NULL; /* clear all local flags associated with the ES */ - es->flags &= ~(ZEBRA_EVPNES_OPER_UP | ZEBRA_EVPNES_BR_PORT); + es->flags &= ~(ZEBRA_EVPNES_OPER_UP | ZEBRA_EVPNES_BR_PORT + | ZEBRA_EVPNES_BYPASS); /* remove from the ES list */ list_delete_node(zmh_info->local_es_list, &es->local_es_listnode); @@ -2493,6 +2673,102 @@ static void zebra_evpn_es_df_pref_update(struct zebra_if *zif, uint16_t df_pref) zebra_evpn_es_send_add_to_client(es); } +/* If bypass mode on an es changed we set all local macs to + * inactive and drop the sync info + */ +static void zebra_evpn_es_bypass_update_macs(struct zebra_evpn_es *es, + struct interface *ifp, bool bypass) +{ + zebra_mac_t *mac; + struct listnode *node; + struct listnode *nnode; + struct zebra_if *zif; + + /* Flush all MACs linked to the ES */ + for (ALL_LIST_ELEMENTS(es->mac_list, node, nnode, mac)) { + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + continue; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("VNI %u mac %pEA %s update es %s", + mac->zevpn->vni, + &mac->macaddr, + bypass ? "bypass" : "non-bypass", + es->esi_str); + zebra_evpn_flush_local_mac(mac, ifp); + } + + /* While in bypass-mode locally learnt MACs are linked + * to the access port instead of the ES + */ + zif = ifp->info; + if (!zif->mac_list) + return; + + for (ALL_LIST_ELEMENTS(zif->mac_list, node, nnode, mac)) { + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + continue; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("VNI %u mac %pEA %s update ifp %s", + mac->zevpn->vni, + &mac->macaddr, + bypass ? "bypass" : "non-bypass", ifp->name); + zebra_evpn_flush_local_mac(mac, ifp); + } +} + +void zebra_evpn_es_bypass_update(struct zebra_evpn_es *es, + struct interface *ifp, bool bypass) +{ + bool old_bypass; + bool dplane_updated; + + old_bypass = !!(es->flags & ZEBRA_EVPNES_BYPASS); + if (old_bypass == bypass) + return; + + if (bypass) + es->flags |= ZEBRA_EVPNES_BYPASS; + else + es->flags &= ~ZEBRA_EVPNES_BYPASS; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("bond %s es %s lacp bypass changed to %s", ifp->name, + es->esi_str, bypass ? "on" : "off"); + + /* send bypass update to BGP */ + if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) + zebra_evpn_es_send_add_to_client(es); + + zebra_evpn_es_bypass_update_macs(es, ifp, bypass); + + /* re-run DF election */ + dplane_updated = zebra_evpn_es_run_df_election(es, __func__); + + /* disable SPH filter */ + if (!dplane_updated && (es->flags & ZEBRA_EVPNES_LOCAL) + && (listcount(es->es_vtep_list) > ES_VTEP_MAX_CNT)) + zebra_evpn_es_br_port_dplane_update(es, __func__); +} + +static void zebra_evpn_es_bypass_cfg_update(struct zebra_if *zif, bool bypass) +{ + bool old_bypass = !!(zif->es_info.flags & ZIF_CFG_ES_FLAG_BYPASS); + + if (old_bypass == bypass) + return; + + if (bypass) + zif->es_info.flags |= ZIF_CFG_ES_FLAG_BYPASS; + else + zif->es_info.flags &= ~ZIF_CFG_ES_FLAG_BYPASS; + + + if (zif->es_info.es) + zebra_evpn_es_bypass_update(zif->es_info.es, zif->ifp, bypass); +} + /* Only certain types of access ports can be setup as an Ethernet Segment */ bool zebra_evpn_is_if_es_capable(struct zebra_if *zif) @@ -2688,7 +2964,7 @@ static void zebra_evpn_es_json_vtep_fill(struct zebra_evpn_es *es, static void zebra_evpn_es_show_entry(struct vty *vty, struct zebra_evpn_es *es, json_object *json_array) { - char type_str[4]; + char type_str[5]; char vtep_str[ES_VTEP_LIST_STR_SZ]; if (json_array) { @@ -2709,6 +2985,8 @@ static void zebra_evpn_es_show_entry(struct vty *vty, struct zebra_evpn_es *es, json_array_string_add(json_flags, "remote"); if (es->flags & ZEBRA_EVPNES_NON_DF) json_array_string_add(json_flags, "nonDF"); + if (es->flags & ZEBRA_EVPNES_BYPASS) + json_array_string_add(json_flags, "bypass"); json_object_object_add(json, "flags", json_flags); } @@ -2730,6 +3008,8 @@ static void zebra_evpn_es_show_entry(struct vty *vty, struct zebra_evpn_es *es, strlcat(type_str, "R", sizeof(type_str)); if (es->flags & ZEBRA_EVPNES_NON_DF) strlcat(type_str, "N", sizeof(type_str)); + if (es->flags & ZEBRA_EVPNES_BYPASS) + strlcat(type_str, "B", sizeof(type_str)); zebra_evpn_es_vtep_str(vtep_str, es, sizeof(vtep_str)); @@ -2767,6 +3047,8 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty, json_array_string_add(json_flags, "remote"); if (es->flags & ZEBRA_EVPNES_NON_DF) json_array_string_add(json_flags, "nonDF"); + if (es->flags & ZEBRA_EVPNES_BYPASS) + json_array_string_add(json_flags, "bypass"); if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) json_array_string_add(json_flags, "readyForBgp"); @@ -2822,6 +3104,8 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty, vty_out(vty, " Ready for BGP: %s\n", (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) ? "yes" : "no"); + if (es->flags & ZEBRA_EVPNES_BYPASS) + vty_out(vty, " LACP bypass: on\n"); vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list)); vty_out(vty, " MAC Count: %d\n", listcount(es->mac_list)); if (es->flags & ZEBRA_EVPNES_LOCAL) @@ -2861,7 +3145,7 @@ void zebra_evpn_es_show(struct vty *vty, bool uj) if (uj) { json_array = json_object_new_array(); } else { - vty_out(vty, "Type: L local, R remote, N non-DF\n"); + vty_out(vty, "Type: B bypass, L local, R remote, N non-DF\n"); vty_out(vty, "%-30s %-4s %-21s %s\n", "ESI", "Type", "ES-IF", "VTEPs"); } @@ -2967,12 +3251,35 @@ int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp) #ifndef VTYSH_EXTRACT_PL #include "zebra/zebra_evpn_mh_clippy.c" #endif +/* CLI for setting an ES in bypass mode */ +DEFPY_HIDDEN(zebra_evpn_es_bypass, zebra_evpn_es_bypass_cmd, + "[no] evpn mh bypass", + NO_STR "EVPN\n" EVPN_MH_VTY_STR "set bypass mode\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif; + + zif = ifp->info; + + if (no) { + zebra_evpn_es_bypass_cfg_update(zif, false); + } else { + if (!zebra_evpn_is_if_es_capable(zif)) { + vty_out(vty, + "%%DF bypass cannot be associated with this interface type\n"); + return CMD_WARNING; + } + zebra_evpn_es_bypass_cfg_update(zif, true); + } + return CMD_SUCCESS; +} + /* 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") + "pref\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct zebra_if *zif; @@ -3637,6 +3944,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); + install_element(INTERFACE_NODE, &zebra_evpn_es_bypass_cmd); install_element(INTERFACE_NODE, &zebra_evpn_mh_uplink_cmd); } diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 81ae740d49..2361a70bff 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -60,6 +60,10 @@ struct zebra_evpn_es { * filter, SPH filter and backup NHG for fast-failover */ #define ZEBRA_EVPNES_BR_PORT (1 << 6) +/* ES is in bypass mode i.e. must not be advertised. ES-bypass is set + * when the associated host bond goes into LACP bypass + */ +#define ZEBRA_EVPNES_BYPASS (1 << 7) /* memory used for adding the es to zmh_info->es_rb_tree */ RB_ENTRY(zebra_evpn_es) rb_node; @@ -180,6 +184,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 +206,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; @@ -256,6 +266,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); @@ -285,6 +301,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 +377,10 @@ 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); +extern void zebra_evpn_es_bypass_update(struct zebra_evpn_es *es, + struct interface *ifp, bool bypass); #endif /* _ZEBRA_EVPN_MH_H */ diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index 1f45b72e3a..dea0fea975 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -327,9 +327,9 @@ int zebra_evpn_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_SVI_IP)) SET_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP); - return zebra_evpn_macip_send_msg_to_client( - vni, macaddr, ip, flags, seq, ZEBRA_NEIGH_ACTIVE, - zmac ? zmac->es : NULL, ZEBRA_MACIP_ADD); + return zebra_evpn_macip_send_msg_to_client(vni, macaddr, ip, flags, seq, + ZEBRA_NEIGH_ACTIVE, zmac->es, + ZEBRA_MACIP_ADD); } /* @@ -1370,7 +1370,7 @@ int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, return -1; } - memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info)); + zebra_evpn_mac_clear_fwd_info(zmac); memset(&zmac->flags, 0, sizeof(uint32_t)); SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO); } else { @@ -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_l2.c b/zebra/zebra_l2.c index c1ad91c8ca..3583c5fbf4 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -110,6 +110,44 @@ void zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave) br_slave->br_if = NULL; } +/* If any of the bond members are in bypass state the bond is placed + * in bypass state + */ +static void zebra_l2_bond_lacp_bypass_eval(struct zebra_if *bond_zif) +{ + struct listnode *node; + struct zebra_if *bond_mbr; + bool old_bypass = !!(bond_zif->flags & ZIF_FLAG_LACP_BYPASS); + bool new_bypass = false; + + if (bond_zif->bond_info.mbr_zifs) { + for (ALL_LIST_ELEMENTS_RO(bond_zif->bond_info.mbr_zifs, node, + bond_mbr)) { + if (bond_mbr->flags & ZIF_FLAG_LACP_BYPASS) { + new_bypass = true; + break; + } + } + } + + if (old_bypass == new_bypass) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT) + zlog_debug("bond %s lacp bypass changed to %s", + bond_zif->ifp->name, new_bypass ? "on" : "off"); + + if (new_bypass) + bond_zif->flags |= ZIF_FLAG_LACP_BYPASS; + else + bond_zif->flags &= ~ZIF_FLAG_LACP_BYPASS; + + if (bond_zif->es_info.es) + zebra_evpn_es_bypass_update(bond_zif->es_info.es, bond_zif->ifp, + new_bypass); +} + +/* Returns true if member was newly linked to bond */ void zebra_l2_map_slave_to_bond(struct zebra_if *zif, vrf_id_t vrf_id) { struct interface *bond_if; @@ -138,6 +176,7 @@ void zebra_l2_map_slave_to_bond(struct zebra_if *zif, vrf_id_t vrf_id) if (zebra_evpn_is_es_bond(bond_if)) zebra_evpn_mh_update_protodown_bond_mbr( zif, false /*clear*/, __func__); + zebra_l2_bond_lacp_bypass_eval(bond_zif); } } else { if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT) @@ -170,6 +209,7 @@ void zebra_l2_unmap_slave_from_bond(struct zebra_if *zif) __func__); listnode_delete(bond_zif->bond_info.mbr_zifs, zif); bond_slave->bond_if = NULL; + zebra_l2_bond_lacp_bypass_eval(bond_zif); } void zebra_l2if_update_bond(struct interface *ifp, bool add) @@ -378,14 +418,36 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp, } } -void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex) +void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex, + bool new_bypass) { struct zebra_if *zif; ifindex_t old_bond_ifindex; + bool old_bypass; + struct zebra_l2info_bondslave *bond_mbr; zif = ifp->info; assert(zif); + old_bypass = !!(zif->flags & ZIF_FLAG_LACP_BYPASS); + if (old_bypass != new_bypass) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT) + zlog_debug("bond-mbr %s lacp bypass changed to %s", + zif->ifp->name, new_bypass ? "on" : "off"); + + if (new_bypass) + zif->flags |= ZIF_FLAG_LACP_BYPASS; + else + zif->flags &= ~ZIF_FLAG_LACP_BYPASS; + + bond_mbr = &zif->bondslave_info; + if (bond_mbr->bond_if) { + struct zebra_if *bond_zif = bond_mbr->bond_if->info; + + zebra_l2_bond_lacp_bypass_eval(bond_zif); + } + } + old_bond_ifindex = zif->bondslave_info.bond_ifindex; if (old_bond_ifindex == bond_ifindex) return; diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index 4b84eb071e..1834430287 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -107,7 +107,7 @@ extern void zebra_l2if_update_bridge_slave(struct interface *ifp, ns_id_t ns_id); extern void zebra_l2if_update_bond_slave(struct interface *ifp, - ifindex_t bond_ifindex); + ifindex_t bond_ifindex, bool bypass); extern void zebra_vlan_bitmap_compute(struct interface *ifp, uint32_t vid_start, uint16_t vid_end); extern void zebra_vlan_mbr_re_eval(struct interface *ifp, diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 424c00d5eb..6cb8010364 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -2852,11 +2852,10 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, vty_out(vty, "Number of MACs (local and remote) known for this VNI: %u\n", num_macs); - vty_out(vty, - "Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n"); - vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %s\n", "MAC", - "Type", "Flags", "Intf/Remote ES/VTEP", - "VLAN", "Seq #'s"); + vty_out(vty, + "Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n"); + vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %s\n", "MAC", "Type", + "Flags", "Intf/Remote ES/VTEP", "VLAN", "Seq #'s"); } else json_object_int_add(json, "numMacs", num_macs); @@ -3503,6 +3502,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"); @@ -4025,6 +4026,7 @@ static int zebra_vxlan_check_del_local_mac(struct interface *ifp, if (!listcount(mac->neigh_list)) { zebra_evpn_mac_del(zevpn, mac); } else { + zebra_evpn_mac_clear_fwd_info(mac); UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); @@ -4121,7 +4123,8 @@ int zebra_vxlan_dp_network_mac_del(struct interface *ifp, if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) zlog_debug("dpDel local-nw-MAC %pEA VNI %u", macaddr, vni); - return zebra_evpn_del_local_mac(zevpn, mac); + + zebra_evpn_del_local_mac(zevpn, mac, false); } return 0; @@ -4158,7 +4161,7 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) return 0; - return zebra_evpn_del_local_mac(zevpn, mac); + return zebra_evpn_del_local_mac(zevpn, mac, false); } /* @@ -4207,7 +4210,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, return zebra_evpn_add_update_local_mac(zvrf, zevpn, ifp, macaddr, vid, sticky, local_inactive, - dp_static); + dp_static, NULL); } /* @@ -4493,6 +4496,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); @@ -4539,10 +4552,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; @@ -4606,6 +4623,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; |
