Merge pull request #7399 from AnuradhaKaruppiah/mh-mac-ecmp-fixes

evpn-mh: miscellaneous fixes in MAC-sync and MAC-ECMP handling
This commit is contained in:
Patrick Ruddy 2020-12-03 16:27:49 +00:00 committed by GitHub
commit dd662ca570
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 680 additions and 120 deletions

View File

@ -2761,10 +2761,6 @@ static ssize_t netlink_neigh_update_msg_encode(
return 0; return 0;
} }
if (nhg_id) {
if (!nl_attr_put32(&req->n, datalen, NDA_NH_ID, nhg_id))
return 0;
}
if (nfy) { if (nfy) {
if (!nl_attr_put(&req->n, datalen, NDA_NOTIFY, if (!nl_attr_put(&req->n, datalen, NDA_NOTIFY,
&nfy_flags, sizeof(nfy_flags))) &nfy_flags, sizeof(nfy_flags)))
@ -2777,9 +2773,16 @@ static ssize_t netlink_neigh_update_msg_encode(
return 0; return 0;
} }
ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN; if (nhg_id) {
if (!nl_attr_put(&req->n, datalen, NDA_DST, &ip->ip.addr, ipa_len)) if (!nl_attr_put32(&req->n, datalen, NDA_NH_ID, nhg_id))
return 0; return 0;
} else {
ipa_len =
IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN;
if (!nl_attr_put(&req->n, datalen, NDA_DST, &ip->ip.addr,
ipa_len))
return 0;
}
if (op == DPLANE_OP_MAC_INSTALL || op == DPLANE_OP_MAC_DELETE) { if (op == DPLANE_OP_MAC_INSTALL || op == DPLANE_OP_MAC_DELETE) {
vlanid_t vid = dplane_ctx_mac_get_vlan(ctx); vlanid_t vid = dplane_ctx_mac_get_vlan(ctx);
@ -2953,8 +2956,9 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
} }
if (IS_ZEBRA_IF_VXLAN(ifp)) if (IS_ZEBRA_IF_VXLAN(ifp))
return zebra_vxlan_check_del_local_mac(ifp, br_if, &mac, return zebra_vxlan_dp_network_mac_add(
vid); ifp, br_if, &mac, vid, nhg_id, sticky,
!!(ndm->ndm_flags & NTF_EXT_LEARNED));
return zebra_vxlan_local_mac_add_update(ifp, br_if, &mac, vid, return zebra_vxlan_local_mac_add_update(ifp, br_if, &mac, vid,
sticky, local_inactive, dp_static); sticky, local_inactive, dp_static);
@ -2982,8 +2986,7 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
} }
if (IS_ZEBRA_IF_VXLAN(ifp)) if (IS_ZEBRA_IF_VXLAN(ifp))
return zebra_vxlan_check_readd_remote_mac(ifp, br_if, &mac, return zebra_vxlan_dp_network_mac_del(ifp, br_if, &mac, vid);
vid);
return zebra_vxlan_local_mac_del(ifp, br_if, &mac, vid); return zebra_vxlan_local_mac_del(ifp, br_if, &mac, vid);
} }

View File

@ -108,9 +108,6 @@ int zebra_evpn_rem_mac_install(zebra_evpn_t *zevpn, zebra_mac_t *mac,
uint32_t nhg_id; uint32_t nhg_id;
struct in_addr vtep_ip; struct in_addr vtep_ip;
if (!(mac->flags & ZEBRA_MAC_REMOTE))
return 0;
zif = zevpn->vxlan_if->info; zif = zevpn->vxlan_if->info;
if (!zif) if (!zif)
return -1; return -1;
@ -155,7 +152,8 @@ int zebra_evpn_rem_mac_install(zebra_evpn_t *zevpn, zebra_mac_t *mac,
/* /*
* Uninstall remote MAC from the forwarding plane. * Uninstall remote MAC from the forwarding plane.
*/ */
int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevpn, zebra_mac_t *mac) int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevpn, zebra_mac_t *mac,
bool force)
{ {
const struct zebra_if *zif, *br_zif; const struct zebra_if *zif, *br_zif;
const struct zebra_l2info_vxlan *vxl; const struct zebra_l2info_vxlan *vxl;
@ -164,8 +162,9 @@ int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevpn, zebra_mac_t *mac)
vlanid_t vid; vlanid_t vid;
enum zebra_dplane_result res; enum zebra_dplane_result res;
if (!(mac->flags & ZEBRA_MAC_REMOTE)) /* If the MAC was not installed there is no need to uninstall it */
return 0; if (!force && mac->es && !(mac->es->flags & ZEBRA_EVPNES_NHG_ACTIVE))
return -1;
if (!zevpn->vxlan_if) { if (!zevpn->vxlan_if) {
if (IS_ZEBRA_DEBUG_VXLAN) if (IS_ZEBRA_DEBUG_VXLAN)
@ -216,7 +215,7 @@ void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevpn, zebra_mac_t *mac)
*/ */
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
&& remote_neigh_count(mac) == 0) { && remote_neigh_count(mac) == 0) {
zebra_evpn_rem_mac_uninstall(zevpn, mac); zebra_evpn_rem_mac_uninstall(zevpn, mac, false /*force*/);
zebra_evpn_es_mac_deref_entry(mac); zebra_evpn_es_mac_deref_entry(mac);
UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
} }
@ -1006,6 +1005,14 @@ int zebra_evpn_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac)
mac->flags); mac->flags);
} }
/* If the MAC is freed before the neigh we will end up
* with a stale pointer against the neigh
*/
if (!list_isempty(mac->neigh_list))
zlog_warn("%s: MAC %pEA flags 0x%x neigh list not empty %d",
__func__, &mac->macaddr, mac->flags,
listcount(mac->neigh_list));
/* force de-ref any ES entry linked to the MAC */ /* force de-ref any ES entry linked to the MAC */
zebra_evpn_es_mac_deref_entry(mac); zebra_evpn_es_mac_deref_entry(mac);
@ -1076,7 +1083,8 @@ static void zebra_evpn_mac_del_hash_entry(struct hash_bucket *bucket, void *arg)
__func__); __func__);
if (mac->flags & ZEBRA_MAC_REMOTE) if (mac->flags & ZEBRA_MAC_REMOTE)
zebra_evpn_rem_mac_uninstall(wctx->zevpn, mac); zebra_evpn_rem_mac_uninstall(wctx->zevpn, mac,
false /*force*/);
} }
zebra_evpn_mac_del(wctx->zevpn, mac); zebra_evpn_mac_del(wctx->zevpn, mac);
@ -1181,10 +1189,9 @@ struct hash *zebra_mac_db_create(const char *desc)
} }
/* program sync mac flags in the dataplane */ /* program sync mac flags in the dataplane */
void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, int zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
bool force_clear_static, const char *caller) bool force_clear_static, const char *caller)
{ {
char macbuf[ETHER_ADDR_STRLEN];
struct interface *ifp; struct interface *ifp;
bool sticky; bool sticky;
bool set_static; bool set_static;
@ -1199,13 +1206,11 @@ void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
if (!ifp) { if (!ifp) {
if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug( zlog_debug(
"%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no access-port", "%s: dp-install sync-mac vni %u mac %pEA es %s 0x%x %sskipped, no access-port",
caller, zevpn->vni, caller, zevpn->vni, &mac->macaddr,
prefix_mac2str(&mac->macaddr, macbuf,
sizeof(macbuf)),
mac->es ? mac->es->esi_str : "-", mac->flags, mac->es ? mac->es->esi_str : "-", mac->flags,
set_inactive ? "inactive " : ""); set_inactive ? "inactive " : "");
return; return -1;
} }
zif = ifp->info; zif = ifp->info;
@ -1213,13 +1218,11 @@ void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
if (!br_ifp) { if (!br_ifp) {
if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug( zlog_debug(
"%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no br", "%s: dp-install sync-mac vni %u mac %pEA es %s 0x%x %sskipped, no br",
caller, zevpn->vni, caller, zevpn->vni, &mac->macaddr,
prefix_mac2str(&mac->macaddr, macbuf,
sizeof(macbuf)),
mac->es ? mac->es->esi_str : "-", mac->flags, mac->es ? mac->es->esi_str : "-", mac->flags,
set_inactive ? "inactive " : ""); set_inactive ? "inactive " : "");
return; return -1;
} }
sticky = !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY); sticky = !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY);
@ -1228,17 +1231,42 @@ void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
else else
set_static = zebra_evpn_mac_is_static(mac); set_static = zebra_evpn_mac_is_static(mac);
/* We can install a local mac that has been synced from the peer
* over the VxLAN-overlay/network-port if fast failover is not
* supported and if the local ES is oper-down.
*/
if (mac->es && zebra_evpn_es_local_mac_via_network_port(mac->es)) {
if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug( zlog_debug(
"dp-install sync-mac vni %u mac %s es %s 0x%x %s%s", "dp-%s sync-nw-mac vni %u mac %pEA es %s 0x%x %s",
zevpn->vni, set_static ? "install" : "uninstall",
prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), zevpn->vni, &mac->macaddr,
mac->es ? mac->es->esi_str : "-", mac->flags,
set_inactive ? "inactive " : "");
if (set_static)
/* XXX - old_static needs to be computed more
* accurately
*/
zebra_evpn_rem_mac_install(zevpn, mac,
true /* old_static */);
else
zebra_evpn_rem_mac_uninstall(zevpn, mac,
false /* force */);
return 0;
}
if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug(
"dp-install sync-mac vni %u mac %pEA es %s 0x%x %s%s",
zevpn->vni, &mac->macaddr,
mac->es ? mac->es->esi_str : "-", mac->flags, mac->es ? mac->es->esi_str : "-", mac->flags,
set_static ? "static " : "", set_static ? "static " : "",
set_inactive ? "inactive " : ""); set_inactive ? "inactive " : "");
dplane_local_mac_add(ifp, br_ifp, vid, &mac->macaddr, sticky, dplane_local_mac_add(ifp, br_ifp, vid, &mac->macaddr, sticky,
set_static, set_inactive); set_static, set_inactive);
return 0;
} }
void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready, void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready,
@ -1694,7 +1722,7 @@ void zebra_evpn_rem_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac)
* go away, we need to uninstall the MAC. * go away, we need to uninstall the MAC.
*/ */
if (remote_neigh_count(mac) == 0) { if (remote_neigh_count(mac) == 0) {
zebra_evpn_rem_mac_uninstall(zevpn, mac); zebra_evpn_rem_mac_uninstall(zevpn, mac, false /*force*/);
zebra_evpn_es_mac_deref_entry(mac); zebra_evpn_es_mac_deref_entry(mac);
UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
} }
@ -2091,13 +2119,12 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
} }
/* if the dataplane thinks the entry is sync but it is /* if the dataplane thinks the entry is sync but it is
* not sync in zebra we need to re-install to fixup * not sync in zebra (or vice-versa) we need to re-install
* to fixup
*/ */
if (dp_static) {
new_static = zebra_evpn_mac_is_static(mac); new_static = zebra_evpn_mac_is_static(mac);
if (!new_static) if (dp_static != new_static)
inform_dataplane = true; inform_dataplane = true;
}
if (local_inactive) if (local_inactive)
SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE);
@ -2193,8 +2220,8 @@ int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
mac, old_bgp_ready, new_bgp_ready); mac, old_bgp_ready, new_bgp_ready);
} }
/* re-install the entry in the kernel */ /* re-install the inactive entry in the kernel */
zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, zebra_evpn_sync_mac_dp_install(mac, true /* set_inactive */,
false /* force_clear_static */, false /* force_clear_static */,
__func__); __func__);

View File

@ -173,7 +173,6 @@ struct sync_mac_ip_ctx {
zebra_mac_t *mac; zebra_mac_t *mac;
}; };
/**************************** SYNC MAC handling *****************************/
/**************************** SYNC MAC handling *****************************/ /**************************** SYNC MAC handling *****************************/
/* if the mac has been added of a mac-route from the peer /* if the mac has been added of a mac-route from the peer
* or if it is being referenced by a neigh added by the * or if it is being referenced by a neigh added by the
@ -204,7 +203,8 @@ static inline void zebra_evpn_mac_clear_sync_info(zebra_mac_t *mac)
struct hash *zebra_mac_db_create(const char *desc); struct hash *zebra_mac_db_create(const char *desc);
uint32_t num_valid_macs(zebra_evpn_t *zevi); uint32_t num_valid_macs(zebra_evpn_t *zevi);
uint32_t num_dup_detected_macs(zebra_evpn_t *zevi); uint32_t num_dup_detected_macs(zebra_evpn_t *zevi);
int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevi, zebra_mac_t *mac); int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevi, zebra_mac_t *mac,
bool force);
int zebra_evpn_rem_mac_install(zebra_evpn_t *zevi, zebra_mac_t *mac, int zebra_evpn_rem_mac_install(zebra_evpn_t *zevi, zebra_mac_t *mac,
bool was_static); bool was_static);
void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevi, zebra_mac_t *mac); void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevi, zebra_mac_t *mac);
@ -218,9 +218,8 @@ int zebra_evpn_macip_send_msg_to_client(uint32_t id, struct ethaddr *macaddr,
void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json); void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json);
void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt); void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt);
void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt); void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt);
void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, int zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
bool force_clear_static, bool force_clear_static, const char *caller);
const char *caller);
void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready, void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready,
bool new_bgp_ready); bool new_bgp_ready);

View File

@ -57,6 +57,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, ZES, "Ethernet Segment");
DEFINE_MTYPE_STATIC(ZEBRA, ZES_EVI, "ES info per-EVI"); DEFINE_MTYPE_STATIC(ZEBRA, ZES_EVI, "ES info per-EVI");
DEFINE_MTYPE_STATIC(ZEBRA, ZMH_INFO, "MH global info"); DEFINE_MTYPE_STATIC(ZEBRA, ZMH_INFO, "MH global info");
DEFINE_MTYPE_STATIC(ZEBRA, ZES_VTEP, "VTEP attached to the ES"); DEFINE_MTYPE_STATIC(ZEBRA, ZES_VTEP, "VTEP attached to the ES");
DEFINE_MTYPE_STATIC(ZEBRA, L2_NH, "L2 nexthop");
static void zebra_evpn_es_get_one_base_evpn(void); static void zebra_evpn_es_get_one_base_evpn(void);
static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es,
@ -965,30 +966,160 @@ void zebra_evpn_if_cleanup(struct zebra_if *zif)
* A L2 NH entry is programmed in the kernel for every ES-VTEP entry. This * A L2 NH entry is programmed in the kernel for every ES-VTEP entry. This
* NH is then added to the L2-ECMP-NHG associated with the ES. * NH is then added to the L2-ECMP-NHG associated with the ES.
*/ */
static uint32_t zebra_evpn_nhid_alloc(bool is_nhg) static uint32_t zebra_evpn_nhid_alloc(struct zebra_evpn_es *es)
{ {
uint32_t id; uint32_t id;
int type; uint32_t nh_id;
bf_assign_index(zmh_info->nh_id_bitmap, id); bf_assign_index(zmh_info->nh_id_bitmap, id);
if (!id) if (!id)
return 0; return 0;
type = is_nhg ? EVPN_NHG_ID_TYPE_BIT : EVPN_NH_ID_TYPE_BIT; if (es) {
return (id | type); nh_id = id | EVPN_NHG_ID_TYPE_BIT;
/* Add to NHG hash */
es->nhg_id = nh_id;
if (!hash_get(zmh_info->nhg_table, es, hash_alloc_intern)) {
bf_release_index(zmh_info->nh_id_bitmap, id);
return 0;
}
} else {
nh_id = id | EVPN_NH_ID_TYPE_BIT;
} }
static void zebra_evpn_nhid_free(uint32_t nh_id) return nh_id;
}
static void zebra_evpn_nhid_free(uint32_t nh_id, struct zebra_evpn_es *es)
{ {
uint32_t id = (nh_id & EVPN_NH_ID_VAL_MASK); uint32_t id = (nh_id & EVPN_NH_ID_VAL_MASK);
if (!id) if (!id)
return; return;
if (es) {
hash_release(zmh_info->nhg_table, es);
es->nhg_id = 0;
}
bf_release_index(zmh_info->nh_id_bitmap, id); bf_release_index(zmh_info->nh_id_bitmap, id);
} }
static unsigned int zebra_evpn_nh_ip_hash_keymake(const void *p)
{
const struct zebra_evpn_l2_nh *nh = p;
return jhash_1word(nh->vtep_ip.s_addr, 0);
}
static bool zebra_evpn_nh_ip_cmp(const void *p1, const void *p2)
{
const struct zebra_evpn_l2_nh *nh1 = p1;
const struct zebra_evpn_l2_nh *nh2 = p2;
if (nh1 == NULL && nh2 == NULL)
return true;
if (nh1 == NULL || nh2 == NULL)
return false;
return (nh1->vtep_ip.s_addr == nh2->vtep_ip.s_addr);
}
static unsigned int zebra_evpn_nhg_hash_keymake(const void *p)
{
const struct zebra_evpn_es *es = p;
return jhash_1word(es->nhg_id, 0);
}
static bool zebra_evpn_nhg_cmp(const void *p1, const void *p2)
{
const struct zebra_evpn_es *es1 = p1;
const struct zebra_evpn_es *es2 = p2;
if (es1 == NULL && es2 == NULL)
return true;
if (es1 == NULL || es2 == NULL)
return false;
return (es1->nhg_id == es2->nhg_id);
}
/* Lookup ES using the NHG id associated with it */
static struct zebra_evpn_es *zebra_evpn_nhg_find(uint32_t nhg_id)
{
struct zebra_evpn_es *es;
struct zebra_evpn_es tmp;
tmp.nhg_id = nhg_id;
es = hash_lookup(zmh_info->nhg_table, &tmp);
return es;
}
/* Returns TRUE if the NHG is associated with a local ES */
bool zebra_evpn_nhg_is_local_es(uint32_t nhg_id,
struct zebra_evpn_es **local_es)
{
struct zebra_evpn_es *es;
es = zebra_evpn_nhg_find(nhg_id);
if (es && (es->flags & ZEBRA_EVPNES_LOCAL)) {
*local_es = es;
return true;
}
*local_es = NULL;
return false;
}
/* update remote macs associated with the ES */
static void zebra_evpn_nhg_mac_update(struct zebra_evpn_es *es)
{
zebra_mac_t *mac;
struct listnode *node;
bool local_via_nw;
local_via_nw = zebra_evpn_es_local_mac_via_network_port(es);
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug("mac update on es %s nhg %s", es->esi_str,
(es->flags & ZEBRA_EVPNES_NHG_ACTIVE)
? "activate"
: "de-activate");
for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) {
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
|| (local_via_nw && CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
&& zebra_evpn_mac_is_static(mac))) {
if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) {
if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug(
"%smac %pEA install via es %s nhg 0x%x",
(mac->flags & ZEBRA_MAC_REMOTE)
? "rem"
: "local-nw",
&mac->macaddr, es->esi_str,
es->nhg_id);
zebra_evpn_rem_mac_install(
mac->zevpn, mac, false /*was_static*/);
} else {
if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug(
"%smac %pEA un-install es %s",
(mac->flags & ZEBRA_MAC_REMOTE)
? "rem"
: "local-nw",
&mac->macaddr, es->esi_str);
zebra_evpn_rem_mac_uninstall(mac->zevpn, mac,
true /*force*/);
}
}
}
}
/* The MAC ECMP group is activated on the first VTEP */ /* The MAC ECMP group is activated on the first VTEP */
static void zebra_evpn_nhg_update(struct zebra_evpn_es *es) static void zebra_evpn_nhg_update(struct zebra_evpn_es *es)
{ {
@ -1001,14 +1132,14 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es)
return; return;
for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
if (!es_vtep->nh_id) if (!es_vtep->nh)
continue; continue;
if (nh_cnt >= ES_VTEP_MAX_CNT) if (nh_cnt >= ES_VTEP_MAX_CNT)
break; break;
memset(&nh_ids[nh_cnt], 0, sizeof(struct nh_grp)); memset(&nh_ids[nh_cnt], 0, sizeof(struct nh_grp));
nh_ids[nh_cnt].id = es_vtep->nh_id; nh_ids[nh_cnt].id = es_vtep->nh->nh_id;
++nh_cnt; ++nh_cnt;
} }
@ -1028,7 +1159,6 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es)
es->nhg_id, nh_str); es->nhg_id, nh_str);
} }
es->flags |= ZEBRA_EVPNES_NHG_ACTIVE;
kernel_upd_mac_nhg(es->nhg_id, nh_cnt, nh_ids); kernel_upd_mac_nhg(es->nhg_id, nh_cnt, nh_ids);
if (!(es->flags & ZEBRA_EVPNES_NHG_ACTIVE)) { if (!(es->flags & ZEBRA_EVPNES_NHG_ACTIVE)) {
es->flags |= ZEBRA_EVPNES_NHG_ACTIVE; es->flags |= ZEBRA_EVPNES_NHG_ACTIVE;
@ -1036,6 +1166,7 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es)
if ((es->flags & ZEBRA_EVPNES_LOCAL)) if ((es->flags & ZEBRA_EVPNES_LOCAL))
zebra_evpn_es_br_port_dplane_update(es, zebra_evpn_es_br_port_dplane_update(es,
__func__); __func__);
zebra_evpn_nhg_mac_update(es);
} }
} else { } else {
if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) { if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) {
@ -1047,52 +1178,161 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es)
if ((es->flags & ZEBRA_EVPNES_LOCAL)) if ((es->flags & ZEBRA_EVPNES_LOCAL))
zebra_evpn_es_br_port_dplane_update(es, zebra_evpn_es_br_port_dplane_update(es,
__func__); __func__);
zebra_evpn_nhg_mac_update(es);
kernel_del_mac_nhg(es->nhg_id); kernel_del_mac_nhg(es->nhg_id);
} }
} }
/* XXX - update remote macs associated with the ES */
} }
static void zebra_evpn_nh_add(struct zebra_evpn_es_vtep *es_vtep) static void zebra_evpn_es_l2_nh_show_entry(struct zebra_evpn_l2_nh *nh,
struct vty *vty,
json_object *json_array)
{ {
if (es_vtep->nh_id) if (json_array) {
json_object *json = NULL;
char ip_buf[INET6_ADDRSTRLEN];
json = json_object_new_object();
json_object_string_add(json, "vtep",
inet_ntop(AF_INET, &nh->vtep_ip, ip_buf,
sizeof(ip_buf)));
json_object_int_add(json, "nhId", nh->nh_id);
json_object_int_add(json, "refCnt", nh->ref_cnt);
json_object_array_add(json_array, json);
} else {
vty_out(vty, "%-16pI4 %-10u %u\n", &nh->vtep_ip, nh->nh_id,
nh->ref_cnt);
}
}
static void zebra_evpn_l2_nh_show_cb(struct hash_bucket *bucket, void *ctxt)
{
struct zebra_evpn_l2_nh *nh = (struct zebra_evpn_l2_nh *)bucket->data;
struct evpn_mh_show_ctx *wctx = (struct evpn_mh_show_ctx *)ctxt;
zebra_evpn_es_l2_nh_show_entry(nh, wctx->vty, wctx->json);
}
void zebra_evpn_l2_nh_show(struct vty *vty, bool uj)
{
struct evpn_mh_show_ctx wctx;
json_object *json_array = NULL;
if (uj) {
json_array = json_object_new_array();
} else {
vty_out(vty, "%-16s %-10s %s\n", "VTEP", "NH id", "#ES");
}
memset(&wctx, 0, sizeof(wctx));
wctx.vty = vty;
wctx.json = json_array;
hash_iterate(zmh_info->nh_ip_table, zebra_evpn_l2_nh_show_cb, &wctx);
if (uj) {
vty_out(vty, "%s\n",
json_object_to_json_string_ext(
json_array, JSON_C_TO_STRING_PRETTY));
json_object_free(json_array);
}
}
static struct zebra_evpn_l2_nh *zebra_evpn_l2_nh_find(struct in_addr vtep_ip)
{
struct zebra_evpn_l2_nh *nh;
struct zebra_evpn_l2_nh tmp;
tmp.vtep_ip.s_addr = vtep_ip.s_addr;
nh = hash_lookup(zmh_info->nh_ip_table, &tmp);
return nh;
}
static struct zebra_evpn_l2_nh *zebra_evpn_l2_nh_alloc(struct in_addr vtep_ip)
{
struct zebra_evpn_l2_nh *nh;
nh = XCALLOC(MTYPE_L2_NH, sizeof(*nh));
nh->vtep_ip = vtep_ip;
if (!hash_get(zmh_info->nh_ip_table, nh, hash_alloc_intern)) {
XFREE(MTYPE_L2_NH, nh);
return NULL;
}
nh->nh_id = zebra_evpn_nhid_alloc(NULL);
if (!nh->nh_id) {
hash_release(zmh_info->nh_ip_table, nh);
XFREE(MTYPE_L2_NH, nh);
return NULL;
}
/* install the NH in the dataplane */
kernel_upd_mac_nh(nh->nh_id, nh->vtep_ip);
return nh;
}
static void zebra_evpn_l2_nh_free(struct zebra_evpn_l2_nh *nh)
{
/* delete the NH from the dataplane */
kernel_del_mac_nh(nh->nh_id);
zebra_evpn_nhid_free(nh->nh_id, NULL);
hash_release(zmh_info->nh_ip_table, nh);
XFREE(MTYPE_L2_NH, nh);
}
static void zebra_evpn_l2_nh_es_vtep_ref(struct zebra_evpn_es_vtep *es_vtep)
{
if (es_vtep->nh)
return; return;
es_vtep->nh_id = zebra_evpn_nhid_alloc(false); es_vtep->nh = zebra_evpn_l2_nh_find(es_vtep->vtep_ip);
if (!es_vtep->nh)
es_vtep->nh = zebra_evpn_l2_nh_alloc(es_vtep->vtep_ip);
if (!es_vtep->nh_id) if (!es_vtep->nh) {
zlog_warn("es %s vtep %pI4 nh ref failed", es_vtep->es->esi_str,
&es_vtep->vtep_ip);
return; return;
}
++es_vtep->nh->ref_cnt;
if (IS_ZEBRA_DEBUG_EVPN_MH_NH) if (IS_ZEBRA_DEBUG_EVPN_MH_NH)
zlog_debug("es %s vtep %pI4 nh %u add", es_vtep->es->esi_str, zlog_debug("es %s vtep %pI4 nh %u ref %u", es_vtep->es->esi_str,
&es_vtep->vtep_ip, es_vtep->nh_id); &es_vtep->vtep_ip, es_vtep->nh->nh_id,
/* install the NH */ es_vtep->nh->ref_cnt);
kernel_upd_mac_nh(es_vtep->nh_id, es_vtep->vtep_ip);
/* add the NH to the parent NHG */ /* add the NH to the parent NHG */
zebra_evpn_nhg_update(es_vtep->es); zebra_evpn_nhg_update(es_vtep->es);
} }
static void zebra_evpn_nh_del(struct zebra_evpn_es_vtep *es_vtep) static void zebra_evpn_l2_nh_es_vtep_deref(struct zebra_evpn_es_vtep *es_vtep)
{ {
uint32_t nh_id; struct zebra_evpn_l2_nh *nh = es_vtep->nh;
if (!es_vtep->nh_id) if (!nh)
return; return;
if (IS_ZEBRA_DEBUG_EVPN_MH_NH) es_vtep->nh = NULL;
zlog_debug("es %s vtep %pI4 nh %u del", es_vtep->es->esi_str, if (nh->ref_cnt)
&es_vtep->vtep_ip, es_vtep->nh_id); --nh->ref_cnt;
nh_id = es_vtep->nh_id; if (IS_ZEBRA_DEBUG_EVPN_MH_NH)
es_vtep->nh_id = 0; zlog_debug("es %s vtep %pI4 nh %u deref %u",
es_vtep->es->esi_str, &es_vtep->vtep_ip, nh->nh_id,
nh->ref_cnt);
/* remove the NH from the parent NHG */ /* remove the NH from the parent NHG */
zebra_evpn_nhg_update(es_vtep->es); zebra_evpn_nhg_update(es_vtep->es);
/* uninstall the NH */
kernel_del_mac_nh(nh_id);
zebra_evpn_nhid_free(nh_id);
/* uninstall the NH */
if (!nh->ref_cnt)
zebra_evpn_l2_nh_free(nh);
} }
/*****************************************************************************/ /*****************************************************************************/
@ -1140,7 +1380,7 @@ static void zebra_evpn_es_vtep_free(struct zebra_evpn_es_vtep *es_vtep)
list_delete_node(es->es_vtep_list, &es_vtep->es_listnode); list_delete_node(es->es_vtep_list, &es_vtep->es_listnode);
/* update the L2-NHG associated with the ES */ /* update the L2-NHG associated with the ES */
zebra_evpn_nh_del(es_vtep); zebra_evpn_l2_nh_es_vtep_deref(es_vtep);
XFREE(MTYPE_ZES_VTEP, es_vtep); XFREE(MTYPE_ZES_VTEP, es_vtep);
} }
@ -1325,7 +1565,7 @@ static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es,
es->esi_str, &vtep_ip); es->esi_str, &vtep_ip);
es_vtep = zebra_evpn_es_vtep_new(es, vtep_ip); es_vtep = zebra_evpn_es_vtep_new(es, vtep_ip);
/* update the L2-NHG associated with the ES */ /* update the L2-NHG associated with the ES */
zebra_evpn_nh_add(es_vtep); zebra_evpn_l2_nh_es_vtep_ref(es_vtep);
} }
old_esr_rxed = !!(es_vtep->flags & ZEBRA_EVPNES_VTEP_RXED_ESR); old_esr_rxed = !!(es_vtep->flags & ZEBRA_EVPNES_VTEP_RXED_ESR);
@ -1422,7 +1662,7 @@ static struct zebra_evpn_es *zebra_evpn_es_new(esi_t *esi)
listset_app_node_mem(es->mac_list); listset_app_node_mem(es->mac_list);
/* reserve a NHG */ /* reserve a NHG */
es->nhg_id = zebra_evpn_nhid_alloc(true); es->nhg_id = zebra_evpn_nhid_alloc(es);
if (IS_ZEBRA_DEBUG_EVPN_MH_ES) if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("es %s nhg %u new", es->esi_str, es->nhg_id); zlog_debug("es %s nhg %u new", es->esi_str, es->nhg_id);
@ -1454,7 +1694,7 @@ static void zebra_evpn_es_free(struct zebra_evpn_es **esp)
es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE; es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE;
kernel_del_mac_nhg(es->nhg_id); kernel_del_mac_nhg(es->nhg_id);
} }
zebra_evpn_nhid_free(es->nhg_id); zebra_evpn_nhid_free(es->nhg_id, es);
/* cleanup resources maintained against the ES */ /* cleanup resources maintained against the ES */
list_delete(&es->es_evi_list); list_delete(&es->es_evi_list);
@ -2213,6 +2453,69 @@ void zebra_evpn_if_es_print(struct vty *vty, struct zebra_if *zif)
vty_out(vty, "%s\n", mh_buf); vty_out(vty, "%s\n", mh_buf);
} }
static void zebra_evpn_local_mac_oper_state_change(struct zebra_evpn_es *es)
{
zebra_mac_t *mac;
struct listnode *node;
/* If fast-failover is supported by the dataplane via the use
* of an ES backup NHG there is nothing to be done in the
* control plane
*/
if (!(zmh_info->flags & ZEBRA_EVPN_MH_REDIRECT_OFF))
return;
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug("mac slow-fail on es %s %s ", es->esi_str,
(es->flags & ZEBRA_EVPNES_OPER_UP) ? "up" : "down");
for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) {
if (!(mac->flags & ZEBRA_MAC_LOCAL)
|| !zebra_evpn_mac_is_static(mac))
continue;
if (es->flags & ZEBRA_EVPNES_OPER_UP) {
if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug(
"VNI %u mac %pEA move to acc %s es %s %s ",
mac->zevpn->vni,
&mac->macaddr,
es->zif->ifp->name, es->esi_str,
(es->flags & ZEBRA_EVPNES_OPER_UP)
? "up"
: "down");
/* switch the local macs to access port */
if (zebra_evpn_sync_mac_dp_install(
mac, false /*set_inactive*/,
false /*force_clear_static*/, __func__)
< 0)
/* if the local mac install fails get rid of the
* old rem entry
*/
zebra_evpn_rem_mac_uninstall(mac->zevpn, mac,
true /*force*/);
} else {
/* switch the local macs to network port. if there
* is no active NHG we don't bother deleting the MAC;
* that is left up to the dataplane to handle.
*/
if (!(es->flags & ZEBRA_EVPNES_NHG_ACTIVE))
continue;
if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug(
"VNI %u mac %pEA move to nhg %u es %s %s ",
mac->zevpn->vni,
&mac->macaddr,
es->nhg_id, es->esi_str,
(es->flags & ZEBRA_EVPNES_OPER_UP)
? "up"
: "down");
zebra_evpn_rem_mac_install(mac->zevpn, mac,
true /*was_static*/);
}
}
}
void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up) void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up)
{ {
struct zebra_evpn_es *es = zif->es_info.es; struct zebra_evpn_es *es = zif->es_info.es;
@ -2231,6 +2534,7 @@ void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up)
es->flags &= ~ZEBRA_EVPNES_OPER_UP; es->flags &= ~ZEBRA_EVPNES_OPER_UP;
zebra_evpn_es_run_df_election(es, __func__); zebra_evpn_es_run_df_election(es, __func__);
zebra_evpn_local_mac_oper_state_change(es);
/* inform BGP of the ES oper state change */ /* inform BGP of the ES oper state change */
if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP)
@ -2286,8 +2590,9 @@ static void zebra_evpn_es_json_vtep_fill(struct zebra_evpn_es *es,
json_object_int_add(json_vtep_entry, "dfPreference", json_object_int_add(json_vtep_entry, "dfPreference",
es_vtep->df_pref); es_vtep->df_pref);
} }
if (es_vtep->nh)
json_object_int_add(json_vtep_entry, "nexthopId", json_object_int_add(json_vtep_entry, "nexthopId",
es_vtep->nh_id); es_vtep->nh->nh_id);
json_object_array_add(json_vteps, json_vtep_entry); json_object_array_add(json_vteps, json_vtep_entry);
} }
} }
@ -2438,7 +2743,8 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty,
alg_buf, alg_buf,
sizeof(alg_buf)), sizeof(alg_buf)),
es_vtep->df_pref); es_vtep->df_pref);
vty_out(vty, " nh: %u\n", es_vtep->nh_id); vty_out(vty, " nh: %u\n",
es_vtep->nh ? es_vtep->nh->nh_id : 0);
} }
vty_out(vty, "\n"); vty_out(vty, "\n");
@ -3136,6 +3442,9 @@ void zebra_evpn_mh_config_write(struct vty *vty)
if (zmh_info->startup_delay_time != ZEBRA_EVPN_MH_STARTUP_DELAY_DEF) if (zmh_info->startup_delay_time != ZEBRA_EVPN_MH_STARTUP_DELAY_DEF)
vty_out(vty, "evpn mh startup-delay %d\n", vty_out(vty, "evpn mh startup-delay %d\n",
zmh_info->startup_delay_time); zmh_info->startup_delay_time);
if (zmh_info->flags & ZEBRA_EVPN_MH_REDIRECT_OFF)
vty_out(vty, "evpn mh redirect-off\n");
} }
int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty, int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty,
@ -3172,6 +3481,19 @@ int zebra_evpn_mh_startup_delay_update(struct vty *vty, uint32_t duration,
return 0; return 0;
} }
int zebra_evpn_mh_redirect_off(struct vty *vty, bool redirect_off)
{
/* This knob needs to be set before ESs are configured
* i.e. cannot be changed on the fly
*/
if (redirect_off)
zmh_info->flags |= ZEBRA_EVPN_MH_REDIRECT_OFF;
else
zmh_info->flags &= ~ZEBRA_EVPN_MH_REDIRECT_OFF;
return 0;
}
void zebra_evpn_interface_init(void) void zebra_evpn_interface_init(void)
{ {
install_element(INTERFACE_NODE, &zebra_evpn_es_id_cmd); install_element(INTERFACE_NODE, &zebra_evpn_es_id_cmd);
@ -3193,6 +3515,11 @@ void zebra_evpn_mh_init(void)
bf_init(zmh_info->nh_id_bitmap, EVPN_NH_ID_MAX); bf_init(zmh_info->nh_id_bitmap, EVPN_NH_ID_MAX);
bf_assign_zero_index(zmh_info->nh_id_bitmap); bf_assign_zero_index(zmh_info->nh_id_bitmap);
zmh_info->nhg_table = hash_create(zebra_evpn_nhg_hash_keymake,
zebra_evpn_nhg_cmp, "l2 NHG table");
zmh_info->nh_ip_table =
hash_create(zebra_evpn_nh_ip_hash_keymake, zebra_evpn_nh_ip_cmp,
"l2 NH IP table");
/* setup broadcast domain tables */ /* setup broadcast domain tables */
zmh_info->evpn_vlan_table = hash_create(zebra_evpn_acc_vl_hash_keymake, zmh_info->evpn_vlan_table = hash_create(zebra_evpn_acc_vl_hash_keymake,
@ -3209,4 +3536,7 @@ void zebra_evpn_mh_terminate(void)
hash_iterate(zmh_info->evpn_vlan_table, hash_iterate(zmh_info->evpn_vlan_table,
zebra_evpn_acc_vl_cleanup_all, NULL); zebra_evpn_acc_vl_cleanup_all, NULL);
hash_free(zmh_info->evpn_vlan_table); hash_free(zmh_info->evpn_vlan_table);
hash_free(zmh_info->nhg_table);
hash_free(zmh_info->nh_ip_table);
bf_free(zmh_info->nh_id_bitmap);
} }

View File

@ -27,6 +27,7 @@
#include "bitfield.h" #include "bitfield.h"
#include "zebra_vxlan.h" #include "zebra_vxlan.h"
#include "zebra_vxlan_private.h" #include "zebra_vxlan_private.h"
#include "zebra_nhg.h"
#define EVPN_MH_VTY_STR "Multihoming\n" #define EVPN_MH_VTY_STR "Multihoming\n"
@ -123,6 +124,19 @@ struct zebra_evpn_es_evi {
struct listnode es_listnode; struct listnode es_listnode;
}; };
/* A single L2 nexthop is allocated across all ESs with the same PE/VTEP
* nexthop
*/
struct zebra_evpn_l2_nh {
struct in_addr vtep_ip;
/* MAC nexthop id */
uint32_t nh_id;
/* es_vtep entries using this nexthop */
uint32_t ref_cnt;
};
/* PE attached to an ES */ /* PE attached to an ES */
struct zebra_evpn_es_vtep { struct zebra_evpn_es_vtep {
struct zebra_evpn_es *es; /* parent ES */ struct zebra_evpn_es *es; /* parent ES */
@ -133,12 +147,12 @@ struct zebra_evpn_es_vtep {
#define ZEBRA_EVPNES_VTEP_RXED_ESR (1 << 0) #define ZEBRA_EVPNES_VTEP_RXED_ESR (1 << 0)
#define ZEBRA_EVPNES_VTEP_DEL_IN_PROG (1 << 1) #define ZEBRA_EVPNES_VTEP_DEL_IN_PROG (1 << 1)
/* MAC nexthop info */
struct zebra_evpn_l2_nh *nh;
/* memory used for adding the entry to es->es_vtep_list */ /* memory used for adding the entry to es->es_vtep_list */
struct listnode es_listnode; struct listnode es_listnode;
/* MAC nexthop */
uint32_t nh_id;
/* Parameters for DF election */ /* Parameters for DF election */
uint8_t df_alg; uint8_t df_alg;
uint32_t df_pref; uint32_t df_pref;
@ -165,6 +179,11 @@ struct zebra_evpn_access_bd {
#define zmh_info (zrouter.mh_info) #define zmh_info (zrouter.mh_info)
struct zebra_evpn_mh_info { struct zebra_evpn_mh_info {
uint32_t flags; uint32_t flags;
/* If the dataplane is not capable of handling a backup NHG on an access
* port we will need to explicitly failover each MAC entry on
* local ES down
*/
#define ZEBRA_EVPN_MH_REDIRECT_OFF (1 << 0)
/* DAD support for EVPN-MH is yet to be added. So on detection of /* DAD support for EVPN-MH is yet to be added. So on detection of
* first local ES, DAD is turned off * first local ES, DAD is turned off
*/ */
@ -187,18 +206,21 @@ struct zebra_evpn_mh_info {
struct in_addr es_originator_ip; struct in_addr es_originator_ip;
/* L2 NH and NHG ids - /* L2 NH and NHG ids -
* Most significant 8 bits is type. Lower 24 bits is the value * Most significant 4 bits is type. Lower 28 bits is the value
* allocated from the nh_id_bitmap. * allocated from the nh_id_bitmap.
*/ */
bitfield_t nh_id_bitmap; bitfield_t nh_id_bitmap;
#define EVPN_NH_ID_MAX (16*1024) #define EVPN_NH_ID_MAX (16*1024)
#define EVPN_NH_ID_VAL_MASK 0xffffff #define EVPN_NH_ID_VAL_MASK 0xffffff
#define EVPN_NH_ID_TYPE_POS 24
/* The purpose of using different types for NHG and NH is NOT to manage the /* The purpose of using different types for NHG and NH is NOT to manage the
* id space separately. It is simply to make debugging easier. * id space separately. It is simply to make debugging easier.
*/ */
#define EVPN_NH_ID_TYPE_BIT (1 << EVPN_NH_ID_TYPE_POS) #define EVPN_NH_ID_TYPE_BIT (NHG_TYPE_L2_NH << NHG_ID_TYPE_POS)
#define EVPN_NHG_ID_TYPE_BIT (2 << EVPN_NH_ID_TYPE_POS) #define EVPN_NHG_ID_TYPE_BIT (NHG_TYPE_L2 << NHG_ID_TYPE_POS)
/* L2-NHG table - key: nhg_id, data: zebra_evpn_es */
struct hash *nhg_table;
/* L2-NH table - key: vtep_up, data: zebra_evpn_nh */
struct hash *nh_ip_table;
/* XXX - re-visit the default hold timer value */ /* XXX - re-visit the default hold timer value */
int mac_hold_time; int mac_hold_time;
@ -234,12 +256,18 @@ static inline bool zebra_evpn_mh_is_fdb_nh(uint32_t id)
(id & EVPN_NH_ID_TYPE_BIT)); (id & EVPN_NH_ID_TYPE_BIT));
} }
static inline bool
zebra_evpn_es_local_mac_via_network_port(struct zebra_evpn_es *es)
{
return !(es->flags & ZEBRA_EVPNES_OPER_UP)
&& (zmh_info->flags & ZEBRA_EVPN_MH_REDIRECT_OFF);
}
static inline bool zebra_evpn_mh_do_dup_addr_detect(void) static inline bool zebra_evpn_mh_do_dup_addr_detect(void)
{ {
return !(zmh_info->flags & ZEBRA_EVPN_MH_DUP_ADDR_DETECT_OFF); return !(zmh_info->flags & ZEBRA_EVPN_MH_DUP_ADDR_DETECT_OFF);
} }
/*****************************************************************************/ /*****************************************************************************/
extern esi_t *zero_esi; extern esi_t *zero_esi;
extern void zebra_evpn_mh_init(void); extern void zebra_evpn_mh_init(void);
@ -296,5 +324,20 @@ extern bool zebra_evpn_is_es_bond(struct interface *ifp);
extern bool zebra_evpn_is_es_bond_member(struct interface *ifp); extern bool zebra_evpn_is_es_bond_member(struct interface *ifp);
extern void zebra_evpn_mh_print(struct vty *vty); extern void zebra_evpn_mh_print(struct vty *vty);
extern void zebra_evpn_mh_json(json_object *json); extern void zebra_evpn_mh_json(json_object *json);
extern bool zebra_evpn_nhg_is_local_es(uint32_t nhg_id,
struct zebra_evpn_es **local_es);
extern int zebra_evpn_mh_redirect_off(struct vty *vty, bool redirect_off);
extern int zebra_evpn_mh_startup_delay_update(struct vty *vty,
uint32_t duration,
bool set_default);
extern void zebra_evpn_mh_uplink_oper_update(struct zebra_if *zif);
extern void zebra_evpn_mh_update_protodown_bond_mbr(struct zebra_if *zif,
bool clear,
const char *caller);
extern bool zebra_evpn_is_es_bond(struct interface *ifp);
extern bool zebra_evpn_is_es_bond_member(struct interface *ifp);
extern void zebra_evpn_mh_print(struct vty *vty);
extern void zebra_evpn_mh_json(json_object *json);
extern void zebra_evpn_l2_nh_show(struct vty *vty, bool uj);
#endif /* _ZEBRA_EVPN_MH_H */ #endif /* _ZEBRA_EVPN_MH_H */

View File

@ -126,6 +126,14 @@ struct nhg_hash_entry {
#define NEXTHOP_GROUP_FPM (1 << 6) #define NEXTHOP_GROUP_FPM (1 << 6)
}; };
/* Upper 4 bits of the NHG are reserved for indicating the NHG type */
#define NHG_ID_TYPE_POS 28
enum nhg_type {
NHG_TYPE_L3 = 0,
NHG_TYPE_L2_NH, /* NHs in a L2 NHG used as a MAC/FDB dest */
NHG_TYPE_L2, /* L2 NHG used as a MAC/FDB dest */
};
/* Was this one we created, either this session or previously? */ /* Was this one we created, either this session or previously? */
#define ZEBRA_NHG_CREATED(NHE) \ #define ZEBRA_NHG_CREATED(NHE) \
(((NHE->type) <= ZEBRA_ROUTE_MAX) && (NHE->type != ZEBRA_ROUTE_KERNEL)) (((NHE->type) <= ZEBRA_ROUTE_MAX) && (NHE->type != ZEBRA_ROUTE_KERNEL))

View File

@ -2502,6 +2502,20 @@ DEFPY (evpn_mh_startup_delay,
no ? true : false); no ? true : false);
} }
DEFPY(evpn_mh_redirect_off, evpn_mh_redirect_off_cmd,
"[no$no] evpn mh redirect-off",
NO_STR
"EVPN\n"
"Multihoming\n"
"ES bond redirect for fast-failover off\n")
{
bool redirect_off;
redirect_off = no ? false : true;
return zebra_evpn_mh_redirect_off(vty, redirect_off);
}
DEFUN (default_vrf_vni_mapping, DEFUN (default_vrf_vni_mapping,
default_vrf_vni_mapping_cmd, default_vrf_vni_mapping_cmd,
"vni " CMD_VNI_RANGE "[prefix-routes-only]", "vni " CMD_VNI_RANGE "[prefix-routes-only]",
@ -2681,6 +2695,21 @@ DEFUN (show_evpn_global,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
DEFPY(show_evpn_l2_nh,
show_evpn_l2_nh_cmd,
"show evpn l2-nh [json$json]",
SHOW_STR
"EVPN\n"
"Layer2 nexthops\n"
JSON_STR)
{
bool uj = !!json;
zebra_evpn_l2_nh_show(vty, uj);
return CMD_SUCCESS;
}
DEFPY(show_evpn_es, DEFPY(show_evpn_es,
show_evpn_es_cmd, show_evpn_es_cmd,
"show evpn es [NAME$esi_str|detail$detail] [json$json]", "show evpn es [NAME$esi_str|detail$detail] [json$json]",
@ -2688,8 +2717,8 @@ DEFPY(show_evpn_es,
"EVPN\n" "EVPN\n"
"Ethernet Segment\n" "Ethernet Segment\n"
"ES ID\n" "ES ID\n"
JSON_STR "Detailed information\n"
"Detailed information\n") JSON_STR)
{ {
esi_t esi; esi_t esi;
bool uj = !!json; bool uj = !!json;
@ -4028,6 +4057,7 @@ void zebra_vty_init(void)
install_element(VIEW_NODE, &show_evpn_vni_cmd); install_element(VIEW_NODE, &show_evpn_vni_cmd);
install_element(VIEW_NODE, &show_evpn_vni_detail_cmd); install_element(VIEW_NODE, &show_evpn_vni_detail_cmd);
install_element(VIEW_NODE, &show_evpn_vni_vni_cmd); install_element(VIEW_NODE, &show_evpn_vni_vni_cmd);
install_element(VIEW_NODE, &show_evpn_l2_nh_cmd);
install_element(VIEW_NODE, &show_evpn_es_cmd); install_element(VIEW_NODE, &show_evpn_es_cmd);
install_element(VIEW_NODE, &show_evpn_es_evi_cmd); install_element(VIEW_NODE, &show_evpn_es_evi_cmd);
install_element(VIEW_NODE, &show_evpn_access_vlan_cmd); install_element(VIEW_NODE, &show_evpn_access_vlan_cmd);
@ -4060,6 +4090,7 @@ void zebra_vty_init(void)
install_element(CONFIG_NODE, &evpn_mh_mac_holdtime_cmd); install_element(CONFIG_NODE, &evpn_mh_mac_holdtime_cmd);
install_element(CONFIG_NODE, &evpn_mh_neigh_holdtime_cmd); install_element(CONFIG_NODE, &evpn_mh_neigh_holdtime_cmd);
install_element(CONFIG_NODE, &evpn_mh_startup_delay_cmd); install_element(CONFIG_NODE, &evpn_mh_startup_delay_cmd);
install_element(CONFIG_NODE, &evpn_mh_redirect_off_cmd);
install_element(CONFIG_NODE, &default_vrf_vni_mapping_cmd); install_element(CONFIG_NODE, &default_vrf_vni_mapping_cmd);
install_element(CONFIG_NODE, &no_default_vrf_vni_mapping_cmd); install_element(CONFIG_NODE, &no_default_vrf_vni_mapping_cmd);
install_element(VRF_NODE, &vrf_vni_mapping_cmd); install_element(VRF_NODE, &vrf_vni_mapping_cmd);

View File

@ -3910,9 +3910,10 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp,
* us, this must involve a multihoming scenario. Treat this as implicit delete * us, this must involve a multihoming scenario. Treat this as implicit delete
* of any prior local MAC. * of any prior local MAC.
*/ */
int zebra_vxlan_check_del_local_mac(struct interface *ifp, static int zebra_vxlan_check_del_local_mac(struct interface *ifp,
struct interface *br_if, struct interface *br_if,
struct ethaddr *macaddr, vlanid_t vid) struct ethaddr *macaddr,
vlanid_t vid)
{ {
struct zebra_if *zif; struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl; struct zebra_l2info_vxlan *vxl;
@ -3969,12 +3970,46 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp,
return 0; return 0;
} }
/* /* MAC notification from the dataplane with a network dest port -
* Handle remote MAC delete by kernel; readd the remote MAC if we have it. * 1. This can be a local MAC on a down ES (if fast-failover is not possible
* This can happen because the remote MAC entries are also added as "dynamic", * 2. Or it can be a remote MAC
* so the kernel can ageout the entry.
*/ */
int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, int zebra_vxlan_dp_network_mac_add(struct interface *ifp,
struct interface *br_if,
struct ethaddr *macaddr, vlanid_t vid,
uint32_t nhg_id, bool sticky, bool dp_static)
{
struct zebra_evpn_es *es;
struct interface *acc_ifp;
/* if remote mac delete the local entry */
if (!nhg_id || !zebra_evpn_nhg_is_local_es(nhg_id, &es)
|| !zebra_evpn_es_local_mac_via_network_port(es)) {
if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug("dpAdd remote MAC %pEA VID %u", macaddr,
vid);
return zebra_vxlan_check_del_local_mac(ifp, br_if, macaddr,
vid);
}
/* If local MAC on a down local ES translate the network-mac-add
* to a local-inactive-mac-add
*/
if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug("dpAdd local-nw-MAC %pEA VID %u", macaddr, vid);
acc_ifp = es->zif->ifp;
return zebra_vxlan_local_mac_add_update(
acc_ifp, br_if, macaddr, vid, sticky,
false /* local_inactive */, dp_static);
}
/*
* Handle network MAC delete by kernel -
* 1. readd the remote MAC if we have it
* 2. local MAC with does ES may also need to be re-installed
*/
static int zebra_vxlan_do_local_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac);
int zebra_vxlan_dp_network_mac_del(struct interface *ifp,
struct interface *br_if, struct interface *br_if,
struct ethaddr *macaddr, vlanid_t vid) struct ethaddr *macaddr, vlanid_t vid)
{ {
@ -3984,7 +4019,6 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp,
zebra_evpn_t *zevpn = NULL; zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL; zebra_l3vni_t *zl3vni = NULL;
zebra_mac_t *mac = NULL; zebra_mac_t *mac = NULL;
char buf[ETHER_ADDR_STRLEN];
zif = ifp->info; zif = ifp->info;
assert(zif); assert(zif);
@ -4010,16 +4044,89 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp,
if (!mac) if (!mac)
return 0; return 0;
/* Is it a remote entry? */ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) /* If remote entry simply re-install */
if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug(
"dpDel remote MAC %pEA intf %s(%u) VNI %u - readd",
macaddr, ifp->name, ifp->ifindex, vni);
zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */);
} else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) && mac->es
&& zebra_evpn_es_local_mac_via_network_port(mac->es)) {
/* If local entry via nw-port call local-del which will
* re-install entry in the dataplane is needed
*/
if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug("dpDel local-nw-MAC %pEA VNI %u", macaddr,
vni);
zebra_vxlan_do_local_mac_del(zevpn, mac);
}
return 0; return 0;
}
static int zebra_vxlan_do_local_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac)
{
bool old_bgp_ready;
bool new_bgp_ready;
if (IS_ZEBRA_DEBUG_VXLAN) if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("Del remote MAC %s intf %s(%u) VNI %u - readd", zlog_debug("DEL MAC %pEA VNI %u seq %u flags 0x%x nbr count %u",
prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, &mac->macaddr, zevpn->vni, mac->loc_seq, mac->flags,
ifp->ifindex, vni); listcount(mac->neigh_list));
old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags);
if (zebra_evpn_mac_is_static(mac)) {
/* this is a synced entry and can only be removed when the
* es-peers stop advertising it.
*/
memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug(
"re-add sync-mac vni %u mac %pEA es %s seq %d f 0x%x",
zevpn->vni, &mac->macaddr,
mac->es ? mac->es->esi_str : "-", mac->loc_seq,
mac->flags);
/* inform-bgp about change in local-activity if any */
if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) {
SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE);
new_bgp_ready =
zebra_evpn_mac_is_ready_for_bgp(mac->flags);
zebra_evpn_mac_send_add_del_to_client(
mac, old_bgp_ready, new_bgp_ready);
}
/* re-install the entry in the kernel */
zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */,
false /* force_clear_static */,
__func__);
return 0;
}
/* Update all the neigh entries associated with this mac */
zebra_evpn_process_neigh_on_local_mac_del(zevpn, mac);
/* Remove MAC from BGP. */
zebra_evpn_mac_send_del_to_client(zevpn->vni, &mac->macaddr, mac->flags,
false /* force */);
zebra_evpn_es_mac_deref_entry(mac);
/*
* If there are no neigh associated with the mac delete the mac
* else mark it as AUTO for forward reference
*/
if (!listcount(mac->neigh_list)) {
zebra_evpn_mac_del(zevpn, mac);
} else {
UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS);
UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
}
zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */);
return 0; return 0;
} }
@ -4030,6 +4137,7 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
struct ethaddr *macaddr, vlanid_t vid) struct ethaddr *macaddr, vlanid_t vid)
{ {
zebra_evpn_t *zevpn; zebra_evpn_t *zevpn;
zebra_mac_t *mac;
/* We are interested in MACs only on ports or (port, VLAN) that /* We are interested in MACs only on ports or (port, VLAN) that
* map to a VNI. * map to a VNI.
@ -4044,7 +4152,16 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
return -1; return -1;
} }
return zebra_evpn_del_local_mac(zevpn, macaddr, ifp); /* If entry doesn't exist, nothing to do. */
mac = zebra_evpn_mac_lookup(zevpn, macaddr);
if (!mac)
return 0;
/* Is it a local entry? */
if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
return 0;
return zebra_vxlan_do_local_mac_del(zevpn, mac);
} }
/* /*

View File

@ -177,13 +177,6 @@ extern int zebra_vxlan_local_mac_add_update(struct interface *ifp,
extern int zebra_vxlan_local_mac_del(struct interface *ifp, extern int zebra_vxlan_local_mac_del(struct interface *ifp,
struct interface *br_if, struct interface *br_if,
struct ethaddr *mac, vlanid_t vid); struct ethaddr *mac, vlanid_t vid);
extern int zebra_vxlan_check_readd_remote_mac(struct interface *ifp,
struct interface *br_if,
struct ethaddr *mac,
vlanid_t vid);
extern int zebra_vxlan_check_del_local_mac(struct interface *ifp,
struct interface *br_if,
struct ethaddr *mac, vlanid_t vid);
extern int zebra_vxlan_check_readd_vtep(struct interface *ifp, extern int zebra_vxlan_check_readd_vtep(struct interface *ifp,
struct in_addr vtep_ip); struct in_addr vtep_ip);
extern int zebra_vxlan_if_up(struct interface *ifp); extern int zebra_vxlan_if_up(struct interface *ifp);
@ -222,6 +215,15 @@ extern void zebra_evpn_init(void);
extern void zebra_vxlan_macvlan_up(struct interface *ifp); extern void zebra_vxlan_macvlan_up(struct interface *ifp);
extern void zebra_vxlan_macvlan_down(struct interface *ifp); extern void zebra_vxlan_macvlan_down(struct interface *ifp);
extern int vni_list_cmp(void *p1, void *p2); extern int vni_list_cmp(void *p1, void *p2);
extern int zebra_vxlan_dp_network_mac_add(struct interface *ifp,
struct interface *br_if,
struct ethaddr *macaddr, vlanid_t vid,
uint32_t nhg_id, bool sticky,
bool dp_static);
extern int zebra_vxlan_dp_network_mac_del(struct interface *ifp,
struct interface *br_if,
struct ethaddr *macaddr,
vlanid_t vid);
#ifdef __cplusplus #ifdef __cplusplus
} }