summaryrefslogtreecommitdiff
path: root/zebra
diff options
context:
space:
mode:
Diffstat (limited to 'zebra')
-rw-r--r--zebra/if_netlink.c31
-rw-r--r--zebra/interface.c10
-rw-r--r--zebra/interface.h14
-rw-r--r--zebra/irdp_interface.c18
-rw-r--r--zebra/rt_netlink.c4
-rw-r--r--zebra/zebra_dplane.c18
-rw-r--r--zebra/zebra_dplane.h5
-rw-r--r--zebra/zebra_evpn.c25
-rw-r--r--zebra/zebra_evpn_mac.c240
-rw-r--r--zebra/zebra_evpn_mac.h34
-rw-r--r--zebra/zebra_evpn_mh.c424
-rw-r--r--zebra/zebra_evpn_mh.h25
-rw-r--r--zebra/zebra_evpn_neigh.c10
-rw-r--r--zebra/zebra_l2.c64
-rw-r--r--zebra/zebra_l2.h2
-rw-r--r--zebra/zebra_vxlan.c36
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;