zebra: uplink tracking and startup delay for EVPN-MH

Local ethernet segments are held in a protodown or error-disabled state
if access to the VxLAN overlay is not ready -
1. When FRR comes up the local-ESs/access-port are kept protodown
for the startup-delay duration. During this time the underlay and
EVPN routes via it are expected to converge.
2. When all the uplinks/core-links attached to the underlay go down
the access-ports are similarly protodowned.

The ES-bond protodown state is propagated to each ES-bond member
and programmed in the dataplane/kernel (per-bond-member).

Configuring uplinks -
vtysh -c "conf t" vtysh -c "interface swp4" vtysh -c "evpn mh uplink"

Configuring startup delay -
vtysh -c "conf t" vtysh -c "evpn mh startup-delay 100"

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
EVPN protodown display -
========================
root@torm-11:mgmt:~# vtysh -c "show evpn"
L2 VNIs: 10
L3 VNIs: 3
Advertise gateway mac-ip: No
Advertise svi mac-ip: No
Duplicate address detection: Disable
  Detection max-moves 5, time 180
EVPN MH:
  mac-holdtime: 60s, neigh-holdtime: 60s
  startup-delay: 180s, start-delay-timer: 00:01:14 <<<<<<<<<<<<
  uplink-cfg-cnt: 4, uplink-active-cnt: 4
  protodown: startup-delay <<<<<<<<<<<<<<<<<<<<<<<
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ES-bond protodown display -
===========================
root@torm-11:mgmt:~# vtysh -c "show interface hostbond1"
Interface hostbond1 is up, line protocol is down
  Link ups:       0    last: (never)
  Link downs:     1    last: 2020/04/26 20:38:03.53
  PTM status: disabled
  vrf: default
  OS Description: Local Node/s torm-11 and Ports swp5 <==> Remote  Node/s hostd-11 and Ports swp1
  index 58 metric 0 mtu 9152 speed 4294967295
  flags: <UP,BROADCAST,MULTICAST>
  Type: Ethernet
  HWaddr: 00:02:00:00:00:35
  Interface Type bond
  Master interface: bridge
  EVPN-MH: ES id 1 ES sysmac 00:00:00:00:01:11
  protodown: off rc: startup-delay  <<<<<<<<<<<<<<<<<
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ES-bond member protodown display -
==================================
root@torm-11:mgmt:~# vtysh -c "show interface swp5"
Interface swp5 is up, line protocol is down
  Link ups:       0    last: (never)
  Link downs:     3    last: 2020/04/26 20:38:03.52
  PTM status: disabled
  vrf: default
  index 7 metric 0 mtu 9152 speed 10000
  flags: <UP,BROADCAST,MULTICAST>
  Type: Ethernet
  HWaddr: 00:02:00:00:00:35
  Interface Type Other
  Master interface: hostbond1
  protodown: on rc: startup-delay <<<<<<<<<<<<<<<<
root@torm-11:mgmt:~#
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Signed-off-by: Anuradha Karuppiah <anuradhak@cumulusnetworks.com>
This commit is contained in:
Anuradha Karuppiah 2020-05-08 19:11:13 -07:00
parent dd51171227
commit c36e442c4b
10 changed files with 744 additions and 40 deletions

View File

@ -691,6 +691,36 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id,
return 0;
}
/* If the interface is and es bond member then it must follow EVPN's
* protodown setting
*/
static void netlink_proc_dplane_if_protodown(struct zebra_if *zif,
bool protodown)
{
bool zif_protodown;
zif_protodown = !!(zif->flags & ZIF_FLAG_PROTODOWN);
if (protodown == zif_protodown)
return;
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("interface %s dplane change, protdown %s",
zif->ifp->name, protodown ? "on" : "off");
if (zebra_evpn_is_es_bond_member(zif->ifp)) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
"bond mbr %s re-instate protdown %s in the dplane",
zif->ifp->name, zif_protodown ? "on" : "off");
netlink_protodown(zif->ifp, zif_protodown);
} else {
if (protodown)
zif->flags |= ZIF_FLAG_PROTODOWN;
else
zif->flags &= ~ZIF_FLAG_PROTODOWN;
}
}
/*
* Called from interface_lookup_netlink(). This function is only used
* during bootstrap.
@ -849,11 +879,20 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
*/
netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA],
1, link_nsid);
if (IS_ZEBRA_IF_BOND(ifp))
zebra_l2if_update_bond(ifp, true);
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);
if (tb[IFLA_PROTO_DOWN]) {
uint8_t protodown;
protodown = *(uint8_t *)RTA_DATA(tb[IFLA_PROTO_DOWN]);
netlink_proc_dplane_if_protodown(zif, !!protodown);
}
return 0;
}
@ -1284,6 +1323,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
uint8_t old_hw_addr[INTERFACE_HWADDR_MAX];
struct zebra_if *zif;
ns_id_t link_nsid = ns_id;
ifindex_t master_infindex = IFINDEX_INTERNAL;
zns = zebra_ns_lookup(ns_id);
ifi = NLMSG_DATA(h);
@ -1373,16 +1413,17 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
if (slave_kind && (strcmp(slave_kind, "vrf") == 0)
&& !vrf_is_backend_netns()) {
zif_slave_type = ZEBRA_IF_SLAVE_VRF;
vrf_id = *(uint32_t *)RTA_DATA(tb[IFLA_MASTER]);
master_infindex = vrf_id =
*(uint32_t *)RTA_DATA(tb[IFLA_MASTER]);
} else if (slave_kind
&& (strcmp(slave_kind, "bridge") == 0)) {
zif_slave_type = ZEBRA_IF_SLAVE_BRIDGE;
bridge_ifindex =
master_infindex = bridge_ifindex =
*(ifindex_t *)RTA_DATA(tb[IFLA_MASTER]);
} else if (slave_kind
&& (strcmp(slave_kind, "bond") == 0)) {
zif_slave_type = ZEBRA_IF_SLAVE_BOND;
bond_ifindex =
master_infindex = bond_ifindex =
*(ifindex_t *)RTA_DATA(tb[IFLA_MASTER]);
} else
zif_slave_type = ZEBRA_IF_SLAVE_OTHER;
@ -1396,7 +1437,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
zlog_debug(
"RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d sl_type %d master %u flags 0x%x",
name, ifi->ifi_index, vrf_id, zif_type,
zif_slave_type, bridge_ifindex,
zif_slave_type, master_infindex,
ifi->ifi_flags);
if (ifp == NULL) {
@ -1446,6 +1487,15 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
ns_id);
else if (IS_ZEBRA_IF_BOND_SLAVE(ifp))
zebra_l2if_update_bond_slave(ifp, bond_ifindex);
if (tb[IFLA_PROTO_DOWN]) {
uint8_t protodown;
protodown = *(uint8_t *)RTA_DATA(
tb[IFLA_PROTO_DOWN]);
netlink_proc_dplane_if_protodown(ifp->info,
!!protodown);
}
} else if (ifp->vrf_id != vrf_id) {
/* VRF change for an interface. */
if (IS_ZEBRA_DEBUG_KERNEL)
@ -1463,7 +1513,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
zlog_debug(
"RTM_NEWLINK update for %s(%u) sl_type %d master %u flags 0x%x",
name, ifp->ifindex, zif_slave_type,
bridge_ifindex, ifi->ifi_flags);
master_infindex, ifi->ifi_flags);
set_ifindex(ifp, ifi->ifi_index, zns);
if (!tb[IFLA_MTU]) {
@ -1542,12 +1592,23 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
netlink_interface_update_l2info(
ifp, linkinfo[IFLA_INFO_DATA],
0, link_nsid);
if (IS_ZEBRA_IF_BOND(ifp))
zebra_l2if_update_bond(ifp, true);
if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave)
zebra_l2if_update_bridge_slave(ifp,
bridge_ifindex,
ns_id);
else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave)
zebra_l2if_update_bond_slave(ifp, bond_ifindex);
if (tb[IFLA_PROTO_DOWN]) {
uint8_t protodown;
protodown = *(uint8_t *)RTA_DATA(
tb[IFLA_PROTO_DOWN]);
netlink_proc_dplane_if_protodown(ifp->info,
!!protodown);
}
}
zif = ifp->info;
@ -1572,6 +1633,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
if (IS_ZEBRA_IF_BOND(ifp))
zebra_l2if_update_bond(ifp, false);
/* Special handling for bridge or VxLAN interfaces. */
if (IS_ZEBRA_IF_BRIDGE(ifp))
zebra_l2_bridge_del(ifp);

View File

@ -1069,6 +1069,9 @@ void if_up(struct interface *ifp)
if (zif->es_info.es)
zebra_evpn_es_if_oper_state_change(zif, true /*up*/);
if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK)
zebra_evpn_mh_uplink_oper_update(zif);
}
/* Interface goes down. We have to manage different behavior of based
@ -1106,6 +1109,9 @@ void if_down(struct interface *ifp)
if (zif->es_info.es)
zebra_evpn_es_if_oper_state_change(zif, false /*up*/);
if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK)
zebra_evpn_mh_uplink_oper_update(zif);
/* Notify to the protocol daemons. */
zebra_interface_down_update(ifp);
@ -1156,6 +1162,18 @@ void zebra_if_update_all_links(void)
if (!ifp)
continue;
zif = ifp->info;
/* update bond-member to bond linkages */
if ((IS_ZEBRA_IF_BOND_SLAVE(ifp))
&& (zif->bondslave_info.bond_ifindex != IFINDEX_INTERNAL)
&& !zif->bondslave_info.bond_if) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("bond mbr %s map to bond %d",
zif->ifp->name,
zif->bondslave_info.bond_ifindex);
zebra_l2_map_slave_to_bond(zif, ifp->vrf_id);
}
/* update SVI linkages */
if ((zif->link_ifindex != IFINDEX_INTERNAL) && !zif->link) {
zif->link = if_lookup_by_index_per_ns(ns,
zif->link_ifindex);
@ -1382,6 +1400,40 @@ static void ifs_dump_brief_vty(struct vty *vty, struct vrf *vrf)
vty_out(vty, "\n");
}
char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc, char *pd_buf,
uint32_t pd_buf_len)
{
bool first = true;
pd_buf[0] = '\0';
snprintf(pd_buf + strlen(pd_buf), pd_buf_len - strlen(pd_buf), "(");
if (protodown_rc & ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY) {
if (first)
first = false;
else
snprintf(pd_buf + strlen(pd_buf),
pd_buf_len - strlen(pd_buf), ",");
snprintf(pd_buf + strlen(pd_buf), pd_buf_len - strlen(pd_buf),
"startup-delay");
}
if (protodown_rc & ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN) {
if (first)
first = false;
else
snprintf(pd_buf + strlen(pd_buf),
pd_buf_len - strlen(pd_buf), ",");
snprintf(pd_buf + strlen(pd_buf), pd_buf_len - strlen(pd_buf),
"uplinks-down");
}
snprintf(pd_buf + strlen(pd_buf), pd_buf_len - strlen(pd_buf), ")");
return pd_buf;
}
/* Interface's information print out to vty interface. */
static void if_dump_vty(struct vty *vty, struct interface *ifp)
{
@ -1391,6 +1443,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
struct route_node *rn;
struct zebra_if *zebra_if;
struct vrf *vrf;
char pd_buf[ZEBRA_PROTODOWN_RC_STR_LEN];
zebra_if = ifp->info;
@ -1545,6 +1598,14 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
}
zebra_evpn_if_es_print(vty, zebra_if);
vty_out(vty, " protodown: %s",
(zebra_if->flags & ZIF_FLAG_PROTODOWN) ? "on" : "off");
if (zebra_if->protodown_rc)
vty_out(vty, " rc: %s\n",
zebra_protodown_rc_str(zebra_if->protodown_rc, pd_buf,
sizeof(pd_buf)));
else
vty_out(vty, "\n");
if (zebra_if->link_ifindex != IFINDEX_INTERNAL) {
if (zebra_if->link)

View File

@ -29,6 +29,7 @@
#include "zebra/zebra_l2.h"
#include "zebra/zebra_nhg_private.h"
#include "zebra/zebra_router.h"
#ifdef __cplusplus
extern "C" {
@ -284,11 +285,24 @@ struct zebra_es_if_info {
struct zebra_evpn_es *es; /* local ES */
};
enum zebra_if_flags {
/* device has been configured as an uplink for
* EVPN multihoming
*/
ZIF_FLAG_EVPN_MH_UPLINK = (1 << 0),
ZIF_FLAG_EVPN_MH_UPLINK_OPER_UP = (1 << 1),
/* Dataplane protodown-on */
ZIF_FLAG_PROTODOWN = (1 << 2)
};
/* `zebra' daemon local interface structure. */
struct zebra_if {
/* back pointer to the interface */
struct interface *ifp;
enum zebra_if_flags flags;
/* Shutdown configuration. */
uint8_t shutdown;
@ -352,6 +366,7 @@ struct zebra_if {
struct zebra_l2info_brslave brslave_info;
struct zebra_l2info_bondslave bondslave_info;
struct zebra_l2info_bond bond_info;
/* ethernet segment */
struct zebra_es_if_info es_info;
@ -359,6 +374,14 @@ struct zebra_if {
/* bitmap of vlans associated with this interface */
bitfield_t vlan_bitmap;
/* An interface can be error-disabled if a protocol (such as EVPN or
* VRRP) detects a problem with keeping it operationally-up.
* If any of the protodown bits are set protodown-on is programmed
* in the dataplane. This results in a carrier/L1 down on the
* physical device.
*/
enum protodown_reasons protodown_rc;
/* Link fields - for sub-interfaces. */
ifindex_t link_ifindex;
struct interface *link;
@ -400,6 +423,9 @@ DECLARE_HOOK(zebra_if_config_wr, (struct vty * vty, struct interface *ifp),
#define IS_ZEBRA_IF_VETH(ifp) \
(((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_VETH)
#define IS_ZEBRA_IF_BOND(ifp) \
(((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_BOND)
#define IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) \
(((struct zebra_if *)(ifp->info))->zif_slave_type \
== ZEBRA_IF_SLAVE_BRIDGE)
@ -463,6 +489,10 @@ extern unsigned int if_nhg_dependents_count(const struct interface *ifp);
extern bool if_nhg_dependents_is_empty(const struct interface *ifp);
extern void vrf_add_update(struct vrf *vrfp);
extern void zebra_l2_map_slave_to_bond(struct zebra_if *zif, vrf_id_t vrf);
extern void zebra_l2_unmap_slave_from_bond(struct zebra_if *zif);
extern char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc,
char *pd_buf, uint32_t pd_buf_len);
#ifdef HAVE_PROC_NET_DEV
extern void ifstat_update_proc(void);

View File

@ -38,6 +38,7 @@
#include "zebra/rib.h"
#include "zebra/rt.h"
#include "zebra/rt_netlink.h"
#include "zebra/if_netlink.h"
#include "zebra/zebra_errors.h"
#include "zebra/zebra_l2.h"
#include "zebra/zebra_memory.h"
@ -65,6 +66,9 @@ static int zebra_evpn_local_es_update(struct zebra_if *zif, uint32_t lid,
struct ethaddr *sysmac);
static bool zebra_evpn_es_br_port_dplane_update(struct zebra_evpn_es *es,
const char *caller);
static void zebra_evpn_mh_uplink_cfg_update(struct zebra_if *zif, bool set);
static void zebra_evpn_mh_update_protodown_es(struct zebra_evpn_es *es);
static void zebra_evpn_mh_clear_protodown_es(struct zebra_evpn_es *es);
esi_t zero_esi_buf, *zero_esi = &zero_esi_buf;
@ -1713,6 +1717,9 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es,
*/
zebra_evpn_es_local_mac_update(es,
false /* force_clear_static */);
/* inherit EVPN protodown flags on the access port */
zebra_evpn_mh_update_protodown_es(es);
}
static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp)
@ -1729,6 +1736,9 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp)
/* 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
*/
@ -2144,12 +2154,34 @@ bool zebra_evpn_is_if_es_capable(struct zebra_if *zif)
void zebra_evpn_if_es_print(struct vty *vty, struct zebra_if *zif)
{
char buf[ETHER_ADDR_STRLEN];
char mh_buf[80];
bool vty_print = false;
if (zif->es_info.lid || !is_zero_mac(&zif->es_info.sysmac))
vty_out(vty, " EVPN MH: ES id %u ES sysmac %s\n",
zif->es_info.lid,
prefix_mac2str(&zif->es_info.sysmac,
buf, sizeof(buf)));
mh_buf[0] = '\0';
snprintf(mh_buf + strlen(mh_buf), sizeof(mh_buf) - strlen(mh_buf),
" EVPN-MH:");
if (zif->es_info.lid || !is_zero_mac(&zif->es_info.sysmac)) {
vty_print = true;
snprintf(
mh_buf + strlen(mh_buf),
sizeof(mh_buf) - strlen(mh_buf),
" ES id %u ES sysmac %s", zif->es_info.lid,
prefix_mac2str(&zif->es_info.sysmac, buf, sizeof(buf)));
}
if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK) {
vty_print = true;
if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK_OPER_UP)
snprintf(mh_buf + strlen(mh_buf),
sizeof(mh_buf) - strlen(mh_buf), " uplink-up");
else
snprintf(mh_buf + strlen(mh_buf),
sizeof(mh_buf) - strlen(mh_buf),
" uplink-down");
}
if (vty_print)
vty_out(vty, "%s\n", mh_buf);
}
void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up)
@ -2478,6 +2510,9 @@ int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp)
if (zif->es_info.df_pref)
vty_out(vty, " evpn mh es-df-pref %u\n", zif->es_info.df_pref);
if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK)
vty_out(vty, " evpn mh uplink\n");
return 0;
}
@ -2600,6 +2635,70 @@ DEFPY(zebra_evpn_es_id,
return CMD_SUCCESS;
}
/* CLI for tagging an interface as an uplink */
DEFPY(zebra_evpn_mh_uplink, zebra_evpn_mh_uplink_cmd, "[no] evpn mh uplink",
NO_STR "EVPN\n" EVPN_MH_VTY_STR "uplink to the VxLAN core\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif;
zif = ifp->info;
zebra_evpn_mh_uplink_cfg_update(zif, no ? false : true);
return CMD_SUCCESS;
}
void zebra_evpn_mh_json(json_object *json)
{
json_object *json_array;
char thread_buf[THREAD_TIMER_STRLEN];
json_object_int_add(json, "macHoldtime", zmh_info->mac_hold_time);
json_object_int_add(json, "neighHoldtime", zmh_info->neigh_hold_time);
json_object_int_add(json, "startupDelay", zmh_info->startup_delay_time);
json_object_string_add(
json, "startupDelayTimer",
thread_timer_to_hhmmss(thread_buf, sizeof(thread_buf),
zmh_info->startup_delay_timer));
json_object_int_add(json, "uplinkConfigCount",
zmh_info->uplink_cfg_cnt);
json_object_int_add(json, "uplinkActiveCount",
zmh_info->uplink_oper_up_cnt);
if (zmh_info->protodown_rc) {
json_array = json_object_new_array();
if (zmh_info->protodown_rc & ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY)
json_object_array_add(
json_array,
json_object_new_string("startupDelay"));
if (zmh_info->protodown_rc & ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN)
json_object_array_add(
json_array,
json_object_new_string("uplinkDown"));
json_object_object_add(json, "protodownReasons", json_array);
}
}
void zebra_evpn_mh_print(struct vty *vty)
{
char pd_buf[ZEBRA_PROTODOWN_RC_STR_LEN];
char thread_buf[THREAD_TIMER_STRLEN];
vty_out(vty, "EVPN MH:\n");
vty_out(vty, " mac-holdtime: %ds, neigh-holdtime: %ds\n",
zmh_info->mac_hold_time, zmh_info->neigh_hold_time);
vty_out(vty, " startup-delay: %ds, start-delay-timer: %s\n",
zmh_info->startup_delay_time,
thread_timer_to_hhmmss(thread_buf, sizeof(thread_buf),
zmh_info->startup_delay_timer));
vty_out(vty, " uplink-cfg-cnt: %u, uplink-active-cnt: %u\n",
zmh_info->uplink_cfg_cnt, zmh_info->uplink_oper_up_cnt);
if (zmh_info->protodown_rc)
vty_out(vty, " protodown: %s\n",
zebra_protodown_rc_str(zmh_info->protodown_rc, pd_buf,
sizeof(pd_buf)));
}
/*****************************************************************************/
/* A base L2-VNI is maintained to derive parameters such as ES originator-IP.
* XXX: once single vxlan device model becomes available this will not be
@ -2705,23 +2804,316 @@ static void zebra_evpn_es_get_one_base_evpn(void)
hash_walk(zvrf->evpn_table, zebra_evpn_es_get_one_base_evpn_cb, NULL);
}
/*****************************************************************************
* local ethernet segments can be error-disabled if the switch is not
* ready to start transmitting traffic via the VxLAN overlay
*/
bool zebra_evpn_is_es_bond(struct interface *ifp)
{
struct zebra_if *zif = ifp->info;
return !!(struct zebra_if *)zif->es_info.es;
}
bool zebra_evpn_is_es_bond_member(struct interface *ifp)
{
struct zebra_if *zif = ifp->info;
return IS_ZEBRA_IF_BOND_SLAVE(zif->ifp) && zif->bondslave_info.bond_if
&& ((struct zebra_if *)zif->bondslave_info.bond_if->info)
->es_info.es;
}
void zebra_evpn_mh_update_protodown_bond_mbr(struct zebra_if *zif, bool clear,
const char *caller)
{
bool old_protodown;
bool new_protodown;
enum protodown_reasons old_protodown_rc = 0;
enum protodown_reasons protodown_rc = 0;
if (!clear) {
struct zebra_if *bond_zif;
bond_zif = zif->bondslave_info.bond_if->info;
protodown_rc = bond_zif->protodown_rc;
}
if (zif->protodown_rc == protodown_rc)
return;
old_protodown = !!(zif->flags & ZIF_FLAG_PROTODOWN);
old_protodown_rc = zif->protodown_rc;
zif->protodown_rc &= ~ZEBRA_PROTODOWN_EVPN_ALL;
zif->protodown_rc |= (protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL);
new_protodown = !!zif->protodown_rc;
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug(
"%s bond mbr %s protodown_rc changed; old 0x%x new 0x%x",
caller, zif->ifp->name, old_protodown_rc,
zif->protodown_rc);
if (old_protodown == new_protodown)
return;
if (new_protodown)
zif->flags |= ZIF_FLAG_PROTODOWN;
else
zif->flags &= ~ZIF_FLAG_PROTODOWN;
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("%s protodown %s", zif->ifp->name,
new_protodown ? "on" : "off");
zebra_if_set_protodown(zif->ifp, new_protodown);
}
/* The bond members inherit the protodown reason code from the bond */
static void zebra_evpn_mh_update_protodown_bond(struct zebra_if *bond_zif)
{
struct zebra_if *zif;
struct listnode *node;
if (!bond_zif->bond_info.mbr_zifs)
return;
for (ALL_LIST_ELEMENTS_RO(bond_zif->bond_info.mbr_zifs, node, zif)) {
zebra_evpn_mh_update_protodown_bond_mbr(zif, false /*clear*/,
__func__);
}
}
/* The global EVPN MH protodown rc is applied to all local ESs */
static void zebra_evpn_mh_update_protodown_es(struct zebra_evpn_es *es)
{
struct zebra_if *zif;
enum protodown_reasons old_protodown_rc;
zif = es->zif;
if ((zif->protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL)
== (zmh_info->protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL))
return;
old_protodown_rc = zif->protodown_rc;
zif->protodown_rc &= ~ZEBRA_PROTODOWN_EVPN_ALL;
zif->protodown_rc |=
(zmh_info->protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL);
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug(
"es %s ifp %s protodown_rc changed; old 0x%x new 0x%x",
es->esi_str, zif->ifp->name, old_protodown_rc,
zif->protodown_rc);
/* update dataplane with the new protodown setting */
zebra_evpn_mh_update_protodown_bond(zif);
}
static void zebra_evpn_mh_clear_protodown_es(struct zebra_evpn_es *es)
{
struct zebra_if *zif;
enum protodown_reasons old_protodown_rc;
zif = es->zif;
if (!(zif->protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL))
return;
old_protodown_rc = zif->protodown_rc;
zif->protodown_rc &= ~ZEBRA_PROTODOWN_EVPN_ALL;
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug(
"clear: es %s ifp %s protodown_rc cleared; old 0x%x new 0x%x",
es->esi_str, zif->ifp->name, old_protodown_rc,
zif->protodown_rc);
/* update dataplane with the new protodown setting */
zebra_evpn_mh_update_protodown_bond(zif);
}
static void zebra_evpn_mh_update_protodown_es_all(void)
{
struct listnode *node;
struct zebra_evpn_es *es;
for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, node, es))
zebra_evpn_mh_update_protodown_es(es);
}
static void zebra_evpn_mh_update_protodown(enum protodown_reasons protodown_rc,
bool set)
{
enum protodown_reasons old_protodown_rc = zmh_info->protodown_rc;
if (set) {
if ((protodown_rc & zmh_info->protodown_rc) == protodown_rc)
return;
zmh_info->protodown_rc |= protodown_rc;
} else {
if (!(protodown_rc & zmh_info->protodown_rc))
return;
zmh_info->protodown_rc &= ~protodown_rc;
}
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("mh protodown_rc changed; old 0x%x new 0x%x",
old_protodown_rc, zmh_info->protodown_rc);
zebra_evpn_mh_update_protodown_es_all();
}
static inline bool zebra_evpn_mh_is_all_uplinks_down(void)
{
return zmh_info->uplink_cfg_cnt && !zmh_info->uplink_oper_up_cnt;
}
static void zebra_evpn_mh_uplink_oper_flags_update(struct zebra_if *zif,
bool set)
{
if (set) {
if (if_is_operative(zif->ifp)) {
if (!(zif->flags & ZIF_FLAG_EVPN_MH_UPLINK_OPER_UP)) {
zif->flags |= ZIF_FLAG_EVPN_MH_UPLINK_OPER_UP;
++zmh_info->uplink_oper_up_cnt;
}
} else {
if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK_OPER_UP) {
zif->flags &= ~ZIF_FLAG_EVPN_MH_UPLINK_OPER_UP;
if (zmh_info->uplink_oper_up_cnt)
--zmh_info->uplink_oper_up_cnt;
}
}
} else {
if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK_OPER_UP) {
zif->flags &= ~ZIF_FLAG_EVPN_MH_UPLINK_OPER_UP;
if (zmh_info->uplink_oper_up_cnt)
--zmh_info->uplink_oper_up_cnt;
}
}
}
static void zebra_evpn_mh_uplink_cfg_update(struct zebra_if *zif, bool set)
{
bool old_protodown = zebra_evpn_mh_is_all_uplinks_down();
bool new_protodown;
if (set) {
if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK)
return;
zif->flags |= ZIF_FLAG_EVPN_MH_UPLINK;
++zmh_info->uplink_cfg_cnt;
} else {
if (!(zif->flags & ZIF_FLAG_EVPN_MH_UPLINK))
return;
zif->flags &= ~ZIF_FLAG_EVPN_MH_UPLINK;
if (zmh_info->uplink_cfg_cnt)
--zmh_info->uplink_cfg_cnt;
}
zebra_evpn_mh_uplink_oper_flags_update(zif, set);
new_protodown = zebra_evpn_mh_is_all_uplinks_down();
if (old_protodown == new_protodown)
return;
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug(
"mh-uplink-cfg-chg on if %s/%d %s uplinks cfg %u up %u",
zif->ifp->name, zif->ifp->ifindex, set ? "set" : "down",
zmh_info->uplink_cfg_cnt, zmh_info->uplink_oper_up_cnt);
zebra_evpn_mh_update_protodown(ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN,
new_protodown);
}
void zebra_evpn_mh_uplink_oper_update(struct zebra_if *zif)
{
bool old_protodown = zebra_evpn_mh_is_all_uplinks_down();
bool new_protodown;
zebra_evpn_mh_uplink_oper_flags_update(zif, true /*set*/);
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug(
"mh-uplink-oper-chg on if %s/%d %s; uplinks cfg %u up %u",
zif->ifp->name, zif->ifp->ifindex,
if_is_operative(zif->ifp) ? "up" : "down",
zmh_info->uplink_cfg_cnt, zmh_info->uplink_oper_up_cnt);
new_protodown = zebra_evpn_mh_is_all_uplinks_down();
if (old_protodown == new_protodown)
return;
zebra_evpn_mh_update_protodown(ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN,
new_protodown);
}
static int zebra_evpn_mh_startup_delay_exp_cb(struct thread *t)
{
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("startup-delay expired");
zebra_evpn_mh_update_protodown(ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY,
false /* set */);
return 0;
}
static void zebra_evpn_mh_startup_delay_timer_start(bool init)
{
/* 1. This timer can be started during init.
* 2. It can also be restarted if it is alreay running and the
* admin wants to increase or decrease its value
*/
if (!init && !zmh_info->startup_delay_timer)
return;
if (zmh_info->startup_delay_timer) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("startup-delay timer cancelled");
thread_cancel(&zmh_info->startup_delay_timer);
zmh_info->startup_delay_timer = NULL;
}
if (zmh_info->startup_delay_time) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("startup-delay timer started for %d sec",
zmh_info->startup_delay_time);
thread_add_timer(zrouter.master,
zebra_evpn_mh_startup_delay_exp_cb, NULL,
zmh_info->startup_delay_time,
&zmh_info->startup_delay_timer);
zebra_evpn_mh_update_protodown(
ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY, true /* set */);
} else {
zebra_evpn_mh_update_protodown(
ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY, false /* set */);
}
}
/*****************************************************************************/
void zebra_evpn_mh_config_write(struct vty *vty)
{
if (zmh_info->mac_hold_time != EVPN_MH_MAC_HOLD_TIME_DEF)
vty_out(vty, "evpn mh mac-holdtime %ld\n",
if (zmh_info->mac_hold_time != ZEBRA_EVPN_MH_MAC_HOLD_TIME_DEF)
vty_out(vty, "evpn mh mac-holdtime %d\n",
zmh_info->mac_hold_time);
if (zmh_info->neigh_hold_time != EVPN_MH_NEIGH_HOLD_TIME_DEF)
vty_out(vty, "evpn mh neigh-holdtime %ld\n",
if (zmh_info->neigh_hold_time != ZEBRA_EVPN_MH_NEIGH_HOLD_TIME_DEF)
vty_out(vty, "evpn mh neigh-holdtime %d\n",
zmh_info->neigh_hold_time);
if (zmh_info->startup_delay_time != ZEBRA_EVPN_MH_STARTUP_DELAY_DEF)
vty_out(vty, "evpn mh startup-delay %d\n",
zmh_info->startup_delay_time);
}
int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty,
uint32_t duration, bool set_default)
{
if (set_default)
duration = EVPN_MH_NEIGH_HOLD_TIME_DEF;
duration = ZEBRA_EVPN_MH_NEIGH_HOLD_TIME_DEF;
zmh_info->neigh_hold_time = duration;
@ -2732,26 +3124,39 @@ int zebra_evpn_mh_mac_holdtime_update(struct vty *vty,
uint32_t duration, bool set_default)
{
if (set_default)
duration = EVPN_MH_MAC_HOLD_TIME_DEF;
duration = ZEBRA_EVPN_MH_MAC_HOLD_TIME_DEF;
zmh_info->mac_hold_time = duration;
return 0;
}
int zebra_evpn_mh_startup_delay_update(struct vty *vty, uint32_t duration,
bool set_default)
{
if (set_default)
duration = ZEBRA_EVPN_MH_STARTUP_DELAY_DEF;
zmh_info->startup_delay_time = duration;
zebra_evpn_mh_startup_delay_timer_start(false /* init */);
return 0;
}
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_mh_uplink_cmd);
}
void zebra_evpn_mh_init(void)
{
zrouter.mh_info = XCALLOC(MTYPE_ZMH_INFO, sizeof(*zrouter.mh_info));
zmh_info->mac_hold_time = EVPN_MH_MAC_HOLD_TIME_DEF;
zmh_info->neigh_hold_time = EVPN_MH_NEIGH_HOLD_TIME_DEF;
zmh_info->mac_hold_time = ZEBRA_EVPN_MH_MAC_HOLD_TIME_DEF;
zmh_info->neigh_hold_time = ZEBRA_EVPN_MH_NEIGH_HOLD_TIME_DEF;
/* setup ES tables */
RB_INIT(zebra_es_rb_head, &zmh_info->es_rb_tree);
zmh_info->local_es_list = list_new();
@ -2763,6 +3168,9 @@ void zebra_evpn_mh_init(void)
/* setup broadcast domain tables */
zmh_info->evpn_vlan_table = hash_create(zebra_evpn_acc_vl_hash_keymake,
zebra_evpn_acc_vl_cmp, "access VLAN hash table");
zmh_info->startup_delay_time = ZEBRA_EVPN_MH_STARTUP_DELAY_DEF;
zebra_evpn_mh_startup_delay_timer_start(true /*init*/);
}
void zebra_evpn_mh_terminate(void)

View File

@ -195,10 +195,25 @@ struct zebra_evpn_mh_info {
#define EVPN_NHG_ID_TYPE_BIT (2 << EVPN_NH_ID_TYPE_POS)
/* XXX - re-visit the default hold timer value */
#define EVPN_MH_MAC_HOLD_TIME_DEF (18 * 60)
long mac_hold_time;
#define EVPN_MH_NEIGH_HOLD_TIME_DEF (18 * 60)
long neigh_hold_time;
int mac_hold_time;
#define ZEBRA_EVPN_MH_MAC_HOLD_TIME_DEF (18 * 60)
int neigh_hold_time;
#define ZEBRA_EVPN_MH_NEIGH_HOLD_TIME_DEF (18 * 60)
/* During this period access ports will be held in a protodown
* state
*/
int startup_delay_time; /* seconds */
#define ZEBRA_EVPN_MH_STARTUP_DELAY_DEF (3 * 60)
struct thread *startup_delay_timer;
/* Number of configured uplinks */
uint32_t uplink_cfg_cnt;
/* Number of operationally-up uplinks */
uint32_t uplink_oper_up_cnt;
/* These protodown bits are inherited by all ES bonds */
enum protodown_reasons protodown_rc;
};
static inline bool zebra_evpn_mac_is_es_local(zebra_mac_t *mac)
@ -258,5 +273,16 @@ void zebra_evpn_mh_config_write(struct vty *vty);
int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty,
uint32_t duration, bool set_default);
void zebra_evpn_es_local_br_port_update(struct zebra_if *zif);
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);
#endif /* _ZEBRA_EVPN_MH_H */

View File

@ -41,6 +41,7 @@
#include "zebra/zebra_memory.h"
#include "zebra/zebra_vrf.h"
#include "zebra/rt_netlink.h"
#include "zebra/interface.h"
#include "zebra/zebra_l2.h"
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_evpn_mh.h"
@ -109,24 +110,99 @@ void zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave)
br_slave->br_if = NULL;
}
void zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave,
vrf_id_t vrf_id)
void zebra_l2_map_slave_to_bond(struct zebra_if *zif, vrf_id_t vrf_id)
{
struct interface *bond_if;
struct zebra_if *bond_zif;
struct zebra_l2info_bondslave *bond_slave = &zif->bondslave_info;
/* TODO: Handle change of master */
bond_if = if_lookup_by_index_all_vrf(bond_slave->bond_ifindex);
if (bond_if)
bond_slave->bond_if = bond_if;
else
bond_slave->bond_if = if_create_ifindex(bond_slave->bond_ifindex,
vrf_id);
if (bond_if == bond_slave->bond_if)
return;
/* unlink the slave from the old master */
zebra_l2_unmap_slave_from_bond(zif);
/* If the bond is present and ready link the bond-member
* to it
*/
if (bond_if && (bond_zif = bond_if->info)) {
if (bond_zif->bond_info.mbr_zifs) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT)
zlog_debug("bond mbr %s linked to %s",
zif->ifp->name, bond_if->name);
bond_slave->bond_if = bond_if;
/* link the slave to the new bond master */
listnode_add(bond_zif->bond_info.mbr_zifs, zif);
/* inherit protodown flags from the es-bond */
if (zebra_evpn_is_es_bond(bond_if))
zebra_evpn_mh_update_protodown_bond_mbr(
zif, false /*clear*/, __func__);
}
} else {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT)
zlog_debug("bond mbr %s link to bond skipped",
zif->ifp->name);
}
}
void zebra_l2_unmap_slave_from_bond(struct zebra_l2info_bondslave *bond_slave)
void zebra_l2_unmap_slave_from_bond(struct zebra_if *zif)
{
if (bond_slave != NULL)
bond_slave->bond_if = NULL;
struct zebra_l2info_bondslave *bond_slave = &zif->bondslave_info;
struct zebra_if *bond_zif;
if (!bond_slave->bond_if) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT)
zlog_debug("bond mbr %s unlink from bond skipped",
zif->ifp->name);
return;
}
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT)
zlog_debug("bond mbr %s un-linked from %s", zif->ifp->name,
bond_slave->bond_if->name);
/* unlink the slave from the bond master */
bond_zif = bond_slave->bond_if->info;
/* clear protodown flags */
if (zebra_evpn_is_es_bond(bond_zif->ifp))
zebra_evpn_mh_update_protodown_bond_mbr(zif, true /*clear*/,
__func__);
listnode_delete(bond_zif->bond_info.mbr_zifs, zif);
bond_slave->bond_if = NULL;
}
void zebra_l2if_update_bond(struct interface *ifp, bool add)
{
struct zebra_if *zif;
struct zebra_l2info_bond *bond;
zif = ifp->info;
assert(zif);
bond = &zif->bond_info;
if (add) {
if (!bond->mbr_zifs) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT)
zlog_debug("bond %s mbr list create",
ifp->name);
bond->mbr_zifs = list_new();
}
} else {
struct listnode *node;
struct listnode *nnode;
struct zebra_if *bond_mbr;
if (!bond->mbr_zifs)
return;
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT)
zlog_debug("bond %s mbr list delete", ifp->name);
for (ALL_LIST_ELEMENTS(bond->mbr_zifs, node, nnode, bond_mbr))
zebra_l2_unmap_slave_from_bond(bond_mbr);
list_delete(&bond->mbr_zifs);
}
}
/*
@ -318,9 +394,9 @@ void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex)
/* Set up or remove link with master */
if (bond_ifindex != IFINDEX_INTERNAL)
zebra_l2_map_slave_to_bond(&zif->bondslave_info, ifp->vrf_id);
zebra_l2_map_slave_to_bond(zif, ifp->vrf_id);
else if (old_bond_ifindex != IFINDEX_INTERNAL)
zebra_l2_unmap_slave_from_bond(&zif->bondslave_info);
zebra_l2_unmap_slave_from_bond(zif);
}
void zebra_vlan_bitmap_compute(struct interface *ifp,

View File

@ -40,6 +40,10 @@ struct zebra_l2info_brslave {
ns_id_t ns_id; /* network namespace where bridge is */
};
struct zebra_l2info_bond {
struct list *mbr_zifs; /* slaves using this bond as a master */
};
/* zebra L2 interface information - bridge interface */
struct zebra_l2info_bridge {
uint8_t vlan_aware; /* VLAN-aware bridge? */
@ -86,10 +90,6 @@ extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave,
struct zebra_ns *zns);
extern void
zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave);
extern void
zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave, vrf_id_t);
extern void
zebra_l2_unmap_slave_from_bond(struct zebra_l2info_bondslave *bond_slave);
extern void zebra_l2_bridge_add_update(struct interface *ifp,
struct zebra_l2info_bridge *bridge_info,
int add);
@ -112,6 +112,7 @@ 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,
bitfield_t vlan_bitmap);
extern void zebra_l2if_update_bond(struct interface *ifp, bool add);
#ifdef __cplusplus
}

View File

@ -61,6 +61,29 @@ enum multicast_mode {
/* on equal value, MRIB wins for last 2 */
};
/* An interface can be error-disabled if a protocol (such as EVPN or
* VRRP) detects a problem with keeping it operationally-up.
* If any of the protodown bits are set protodown-on is programmed
* in the dataplane. This results in a carrier/L1 down on the
* physical device.
*/
enum protodown_reasons {
/* On startup local ESs are held down for some time to
* allow the underlay to converge and EVPN routes to
* get learnt
*/
ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY = (1 << 0),
/* If all the uplinks are down the switch has lost access
* to the VxLAN overlay and must shut down the access
* ports to allow servers to re-direct their traffic to
* other switches on the Ethernet Segment
*/
ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN = (1 << 1),
ZEBRA_PROTODOWN_EVPN_ALL = (ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN
| ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY)
};
#define ZEBRA_PROTODOWN_RC_STR_LEN 80
struct zebra_mlag_info {
/* Role this zebra router is playing */
enum mlag_role role;

View File

@ -2433,6 +2433,20 @@ DEFPY (evpn_mh_neigh_holdtime,
no ? true : false);
}
DEFPY (evpn_mh_startup_delay,
evpn_mh_startup_delay_cmd,
"[no] evpn mh startup-delay(0-3600)$duration",
NO_STR
"EVPN\n"
"Multihoming\n"
"Startup delay\n"
"duration in seconds\n")
{
return zebra_evpn_mh_startup_delay_update(vty, duration,
no ? true : false);
}
DEFUN (default_vrf_vni_mapping,
default_vrf_vni_mapping_cmd,
"vni " CMD_VNI_RANGE "[prefix-routes-only]",
@ -3990,6 +4004,7 @@ void zebra_vty_init(void)
install_element(CONFIG_NODE, &evpn_mh_mac_holdtime_cmd);
install_element(CONFIG_NODE, &evpn_mh_neigh_holdtime_cmd);
install_element(CONFIG_NODE, &evpn_mh_startup_delay_cmd);
install_element(CONFIG_NODE, &default_vrf_vni_mapping_cmd);
install_element(CONFIG_NODE, &no_default_vrf_vni_mapping_cmd);
install_element(VRF_NODE, &vrf_vni_mapping_cmd);

View File

@ -3427,7 +3427,7 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj)
json_object_int_add(json, "detectionTime", zvrf->dad_time);
json_object_int_add(json, "detectionFreezeTime",
zvrf->dad_freeze_time);
zebra_evpn_mh_json(json);
} else {
vty_out(vty, "L2 VNIs: %u\n", num_l2vnis);
vty_out(vty, "L3 VNIs: %u\n", num_l3vnis);
@ -3447,6 +3447,7 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj)
vty_out(vty, " Detection freeze %s\n",
"permanent");
}
zebra_evpn_mh_print(vty);
}
if (uj) {