DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor");
DEFINE_MTYPE_STATIC(ZEBRA, ZVXLAN_SG, "zebra VxLAN multicast group");
+DEFINE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
+ bool delete, const char *reason), (rmac, zl3vni, delete, reason))
+
/* definitions */
/* PMSI strings. */
#define VXLAN_FLOOD_STR_NO_INFO "-"
static void *zl3vni_alloc(void *p);
static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id);
static int zl3vni_del(zebra_l3vni_t *zl3vni);
-static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t);
-static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni);
-static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni);
static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni);
static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni);
struct in_addr mcast_grp);
static void zebra_vxlan_sg_cleanup(struct hash_backet *backet, void *arg);
+static void zvni_send_mac_to_client(zebra_vni_t *zvn);
+static void zvni_send_neigh_to_client(zebra_vni_t *zvni);
+
/* Private functions */
static int host_rb_entry_compare(const struct host_rb_entry *hle1,
const struct host_rb_entry *hle2)
bool flags_present = false;
struct zebra_vrf *zvrf = NULL;
struct timeval detect_start_time = {0, 0};
+ char timebuf[MONOTIME_STRLEN];
zvrf = zebra_vrf_get_evpn();
if (!zvrf)
if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) {
vty_out(vty, " Duplicate, detected at %s",
- time_to_string(n->dad_dup_detect_time));
+ time_to_string(n->dad_dup_detect_time,
+ timebuf));
} else if (n->dad_count) {
monotime_since(&n->detect_start_time,
&detect_start_time);
if (detect_start_time.tv_sec <= zvrf->dad_time) {
- char *buf = time_to_string(
- n->detect_start_time.tv_sec);
- char tmp_buf[30];
-
- strlcpy(tmp_buf, buf, sizeof(tmp_buf));
+ time_to_string(n->detect_start_time.tv_sec,
+ timebuf);
vty_out(vty,
" Duplicate detection started at %s, detection count %u\n",
- tmp_buf, n->dad_count);
+ timebuf, n->dad_count);
}
}
} else {
return;
if (json_vni == NULL) {
- vty_out(vty, "%*s %-6s %-8s %-17s\n",
+ vty_out(vty, "%*s %-6s %-8s %-17s %u/%u\n",
-wctx->addr_width, buf2, "local",
- state_str, buf1);
+ state_str, buf1, n->loc_seq, n->rem_seq);
} else {
json_object_string_add(json_row, "type", "local");
json_object_string_add(json_row, "state", state_str);
if (json_vni == NULL) {
if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) &&
(wctx->count == 0))
- vty_out(vty,
- "%*s %-6s %-8s %-17s %-21s\n",
+ vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n",
-wctx->addr_width, "Neighbor", "Type",
- "State", "MAC", "Remote VTEP");
- vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n",
+ "State", "MAC", "Remote VTEP",
+ "Seq #'s");
+ vty_out(vty, "%*s %-6s %-8s %-17s %-21s %u/%u\n",
-wctx->addr_width, buf2, "remote", state_str,
- buf1, inet_ntoa(n->r_vtep_ip));
+ buf1, inet_ntoa(n->r_vtep_ip), n->loc_seq, n->rem_seq);
} else {
json_object_string_add(json_row, "type", "remote");
json_object_string_add(json_row, "state", state_str);
hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
if (json == NULL) {
- vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n",
+ vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n",
-wctx.addr_width, "IP", "Type",
- "State", "MAC", "Remote VTEP");
+ "State", "MAC", "Remote VTEP", "Seq #'s");
}
if (print_dup)
hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash,
char buf2[INET6_ADDRSTRLEN];
struct zebra_vrf *zvrf;
struct timeval detect_start_time = {0, 0};
+ char timebuf[MONOTIME_STRLEN];
zvrf = zebra_vrf_get_evpn();
if (!zvrf)
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
vty_out(vty, " Duplicate, detected at %s",
- time_to_string(mac->dad_dup_detect_time));
+ time_to_string(mac->dad_dup_detect_time,
+ timebuf));
} else if (mac->dad_count) {
monotime_since(&mac->detect_start_time,
&detect_start_time);
if (detect_start_time.tv_sec <= zvrf->dad_time) {
- char *buf = time_to_string(
- mac->detect_start_time.tv_sec);
- char tmp_buf[30];
-
- strlcpy(tmp_buf, buf, sizeof(tmp_buf));
+ time_to_string(mac->detect_start_time.tv_sec,
+ timebuf);
vty_out(vty,
" Duplicate detection started at %s, detection count %u\n",
- tmp_buf, mac->dad_count);
+ timebuf, mac->dad_count);
}
}
vty_out(vty, " %-5u", vid);
else
json_object_int_add(json_mac, "vlan", vid);
- }
+ } else /* No vid? fill out the space */
+ if (json_mac_hdr == NULL)
+ vty_out(vty, " %-5s", "");
if (json_mac_hdr == NULL) {
+ vty_out(vty, " %u/%u", mac->loc_seq, mac->rem_seq);
vty_out(vty, "\n");
} else {
json_object_int_add(json_mac, "localSequence",
if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) &&
(wctx->count == 0)) {
vty_out(vty, "\nVNI %u\n\n", wctx->zvni->vni);
- vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC",
- "Type", "Intf/Remote VTEP", "VLAN");
+ vty_out(vty, "%-17s %-6s %-21s %-5s %s\n",
+ "MAC", "Type", "Intf/Remote VTEP",
+ "VLAN", "Seq #'s");
}
- vty_out(vty, "%-17s %-6s %-21s\n", buf1, "remote",
- inet_ntoa(mac->fwd_info.r_vtep_ip));
+ vty_out(vty, "%-17s %-6s %-21s %-5s %u/%u\n", buf1,
+ "remote", inet_ntoa(mac->fwd_info.r_vtep_ip),
+ "", mac->loc_seq, mac->rem_seq);
} else {
json_object_string_add(json_mac, "type", "remote");
json_object_string_add(json_mac, "remoteVtep",
if (json == NULL) {
vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n",
zvni->vni, num_macs);
- vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type",
- "Intf/Remote VTEP", "VLAN");
+ vty_out(vty, "%-17s %-6s %-21s %-5s %s\n", "MAC",
+ "Type", "Intf/Remote VTEP", "VLAN", "Seq #'s");
} else
json_object_int_add(json_vni, "numMacs", num_macs);
}
/* Add primary SVI MAC*/
zvni = (zebra_vni_t *)bucket->data;
+ /* Global (Zvrf) advertise-default-gw is disabled,
+ * but zvni advertise-default-gw is enabled
+ */
+ if (zvni->advertise_gw_macip) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("VNI: %u GW-MACIP enabled, retain gw-macip",
+ zvni->vni);
+ return;
+ }
+
ifp = zvni->vxlan_if;
if (!ifp)
return;
if (!zvni)
return;
+ /* Global(vrf) advertise-svi-ip disabled, but zvni advertise-svi-ip
+ * enabled
+ */
+ if (zvni->advertise_svi_macip) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("VNI: %u SVI-MACIP enabled, retain svi-macip",
+ zvni->vni);
+ return;
+ }
+
ifp = zvni->vxlan_if;
if (!ifp)
return;
ifp->name, ifp->ifindex, vni,
inet_ntoa(vxl->vtep_ip));
- /* VNI hash entry is not expected to exist. */
+ /* VNI hash entry is expected to exist, if the BGP process is killed */
zvni = zvni_lookup(vni);
if (zvni) {
zlog_debug(
"VNI hash already present for IF %s(%u) L2-VNI %u",
ifp->name, ifp->ifindex, vni);
- continue;
- }
- zvni = zvni_add(vni);
- if (!zvni) {
- zlog_debug(
- "Failed to add VNI hash, IF %s(%u) L2-VNI %u",
- ifp->name, ifp->ifindex, vni);
- return;
- }
+ /*
+ * Inform BGP if intf is up and mapped to
+ * bridge.
+ */
+ if (if_is_operative(ifp) &&
+ zif->brslave_info.br_if)
+ zvni_send_add_to_client(zvni);
- if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr ||
- zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) {
- zebra_vxlan_sg_deref(zvni->local_vtep_ip,
- zvni->mcast_grp);
- zebra_vxlan_sg_ref(vxl->vtep_ip,
- vxl->mcast_grp);
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->mcast_grp = vxl->mcast_grp;
- }
- zvni->vxlan_if = ifp;
- vlan_if = zvni_map_to_svi(vxl->access_vlan,
- zif->brslave_info.br_if);
- if (vlan_if) {
- zvni->vrf_id = vlan_if->vrf_id;
- zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
- if (zl3vni)
- listnode_add_sort(zl3vni->l2vnis, zvni);
- }
+ /* Send Local MAC-entries to client */
+ zvni_send_mac_to_client(zvni);
+ /* Send Loval Neighbor entries to client */
+ zvni_send_neigh_to_client(zvni);
+ } else {
+ zvni = zvni_add(vni);
+ if (!zvni) {
+ zlog_debug(
+ "Failed to add VNI hash, IF %s(%u) L2-VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return;
+ }
+
+ if (zvni->local_vtep_ip.s_addr !=
+ vxl->vtep_ip.s_addr ||
+ zvni->mcast_grp.s_addr !=
+ vxl->mcast_grp.s_addr) {
+ zebra_vxlan_sg_deref(
+ zvni->local_vtep_ip,
+ zvni->mcast_grp);
+ zebra_vxlan_sg_ref(vxl->vtep_ip,
+ vxl->mcast_grp);
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->mcast_grp = vxl->mcast_grp;
+ }
+ zvni->vxlan_if = ifp;
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if) {
+ zvni->vrf_id = vlan_if->vrf_id;
+ zl3vni = zl3vni_from_vrf(
+ vlan_if->vrf_id);
+ if (zl3vni)
+ listnode_add_sort(
+ zl3vni->l2vnis, zvni);
+ }
- /* Inform BGP if intf is up and mapped to bridge. */
- if (if_is_operative(ifp) && zif->brslave_info.br_if)
- zvni_send_add_to_client(zvni);
+ /*
+ * Inform BGP if intf is up and mapped to
+ * bridge.
+ */
+ if (if_is_operative(ifp) &&
+ zif->brslave_info.br_if)
+ zvni_send_add_to_client(zvni);
+ }
}
}
}
memset(&zrmac->fwd_info, 0, sizeof(zrmac->fwd_info));
zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4;
+ /* Send RMAC for FPM processing */
+ hook_call(zebra_rmac_update, zrmac, zl3vni, false,
+ "new RMAC added");
+
/* install rmac in kernel */
zl3vni_rmac_install(zl3vni, zrmac);
}
/* uninstall from kernel */
zl3vni_rmac_uninstall(zl3vni, zrmac);
+ /* Send RMAC for FPM processing */
+ hook_call(zebra_rmac_update, zrmac, zl3vni, true,
+ "RMAC deleted");
+
/* del the rmac entry */
zl3vni_rmac_del(zl3vni, zrmac);
}
return 0;
}
-static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni)
+struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni)
{
struct zebra_ns *zns = NULL;
struct route_node *rn = NULL;
return NULL;
}
-static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni)
+struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni)
{
struct zebra_if *zif = NULL; /* zebra_if for vxlan_if */
struct zebra_l2info_vxlan *vxl = NULL; /* l2 info for vxlan_if */
return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
}
-static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id)
+zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id)
{
struct zebra_vrf *zvrf = NULL;
zrmac = (zebra_mac_t *)bucket->data;
zl3vni = (zebra_l3vni_t *)ctx;
zl3vni_rmac_uninstall(zl3vni, zrmac);
+
+ /* Send RMAC for FPM processing */
+ hook_call(zebra_rmac_update, zrmac, zl3vni, true, "RMAC deleted");
+
zl3vni_rmac_del(zl3vni, zrmac);
}
vty_out(vty,
"Number of ARPs (local and remote) known for this VNI: %u\n",
num_neigh);
- vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n",
- -wctx.addr_width, "IP", "Type",
- "State", "MAC", "Remote VTEP");
+ vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n", -wctx.addr_width,
+ "IP", "Type", "State", "MAC", "Remote VTEP", "Seq #'s");
} else
json_object_int_add(json, "numArpNd", num_neigh);
vty_out(vty,
"Number of MACs (local and remote) known for this VNI: %u\n",
num_macs);
- vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type",
- "Intf/Remote VTEP", "VLAN");
+ vty_out(vty, "%-17s %-6s %-21s %-5s %s\n", "MAC", "Type",
+ "Intf/Remote VTEP", "VLAN", "Seq #'s");
} else
json_object_int_add(json, "numMacs", num_macs);
struct interface *ifp = NULL;
if (!EVPN_ENABLED(zvrf)) {
- zlog_debug("EVPN GW-MACIP Adv for non-EVPN VRF %u",
+ zlog_debug("EVPN SVI-MACIP Adv for non-EVPN VRF %u",
zvrf_id(zvrf));
return;
}
if (!vni) {
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("EVPN gateway macip Adv %s, currently %s",
+ zlog_debug("EVPN SVI-MACIP Adv %s, currently %s",
advertise ? "enabled" : "disabled",
advertise_gw_macip_enabled(NULL)
? "enabled"
/************************** vxlan SG cache management ************************/
/* Inform PIM about the mcast group */
-static int zebra_vxlan_sg_send(struct prefix_sg *sg,
- char *sg_str, uint16_t cmd)
+static int zebra_vxlan_sg_send(struct zebra_vrf *zvrf,
+ struct prefix_sg *sg,
+ char *sg_str, uint16_t cmd)
{
struct zserv *client = NULL;
struct stream *s = NULL;
if (!client)
return 0;
+ if (!CHECK_FLAG(zvrf->flags, ZEBRA_PIM_SEND_VXLAN_SG))
+ return 0;
+
s = stream_new(ZEBRA_MAX_PACKET_SIZ);
zclient_create_header(s, cmd, VRF_DEFAULT);
return vxlan_sg;
}
- zebra_vxlan_sg_send(sg, vxlan_sg->sg_str, ZEBRA_VXLAN_SG_ADD);
+ zebra_vxlan_sg_send(zvrf, sg, vxlan_sg->sg_str,
+ ZEBRA_VXLAN_SG_ADD);
return vxlan_sg;
}
zebra_vxlan_sg_do_deref(zvrf, sip, vxlan_sg->sg.grp);
}
- zebra_vxlan_sg_send(&vxlan_sg->sg, vxlan_sg->sg_str,
- ZEBRA_VXLAN_SG_DEL);
+ zebra_vxlan_sg_send(zvrf, &vxlan_sg->sg,
+ vxlan_sg->sg_str, ZEBRA_VXLAN_SG_DEL);
hash_release(vxlan_sg->zvrf->vxlan_sg_table, vxlan_sg);
zebra_vxlan_sg_del(vxlan_sg);
}
+
+static void zebra_vxlan_sg_replay_send(struct hash_backet *backet, void *arg)
+{
+ zebra_vxlan_sg_t *vxlan_sg = (zebra_vxlan_sg_t *)backet->data;
+
+ zebra_vxlan_sg_send(vxlan_sg->zvrf, &vxlan_sg->sg,
+ vxlan_sg->sg_str, ZEBRA_VXLAN_SG_ADD);
+}
+
+/* Handle message from client to replay vxlan SG entries */
+void zebra_vxlan_sg_replay(ZAPI_HANDLER_ARGS)
+{
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("VxLAN SG updates to PIM, start");
+
+ SET_FLAG(zvrf->flags, ZEBRA_PIM_SEND_VXLAN_SG);
+
+ if (!EVPN_ENABLED(zvrf)) {
+ zlog_debug("VxLAN SG replay request on unexpected vrf %d",
+ zvrf->vrf->vrf_id);
+ return;
+ }
+
+ hash_iterate(zvrf->vxlan_sg_table, zebra_vxlan_sg_replay_send, NULL);
+}
+
+/************************** EVPN BGP config management ************************/
+/* Notify Local MACs to the clienti, skips GW MAC */
+static void zvni_send_mac_hash_entry_to_client(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct mac_walk_ctx *wctx = arg;
+ zebra_mac_t *zmac = bucket->data;
+
+ if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_DEF_GW))
+ return;
+
+ if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL))
+ zvni_mac_send_add_to_client(wctx->zvni->vni, &zmac->macaddr,
+ zmac->flags, zmac->loc_seq);
+}
+
+/* Iterator to Notify Local MACs of a L2VNI */
+static void zvni_send_mac_to_client(zebra_vni_t *zvni)
+{
+ struct mac_walk_ctx wctx;
+
+ if (!zvni->mac_table)
+ return;
+
+ memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+ wctx.zvni = zvni;
+
+ hash_iterate(zvni->mac_table, zvni_send_mac_hash_entry_to_client,
+ &wctx);
+}
+
+/* Notify Neighbor entries to the Client, skips the GW entry */
+static void zvni_send_neigh_hash_entry_to_client(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct mac_walk_ctx *wctx = arg;
+ zebra_neigh_t *zn = bucket->data;
+ zebra_mac_t *zmac = NULL;
+
+ if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_DEF_GW))
+ return;
+
+ if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_LOCAL) &&
+ IS_ZEBRA_NEIGH_ACTIVE(zn)) {
+ zmac = zvni_mac_lookup(wctx->zvni, &zn->emac);
+ if (!zmac)
+ return;
+
+ zvni_neigh_send_add_to_client(wctx->zvni->vni, &zn->ip,
+ &zn->emac, zn->flags,
+ zn->loc_seq);
+ }
+}
+
+/* Iterator of a specific L2VNI */
+static void zvni_send_neigh_to_client(zebra_vni_t *zvni)
+{
+ struct neigh_walk_ctx wctx;
+
+ memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ wctx.zvni = zvni;
+
+ hash_iterate(zvni->neigh_table, zvni_send_neigh_hash_entry_to_client,
+ &wctx);
+}
+
+static void zvni_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt)
+{
+ zebra_vni_t *zvni = NULL;
+
+ zvni = (zebra_vni_t *)bucket->data;
+ zvni->advertise_gw_macip = 0;
+ zvni->advertise_svi_macip = 0;
+ zvni->advertise_subnet = 0;
+
+ zvni_neigh_del_all(zvni, 1, 0,
+ DEL_REMOTE_NEIGH | DEL_REMOTE_NEIGH_FROM_VTEP);
+ zvni_mac_del_all(zvni, 1, 0,
+ DEL_REMOTE_MAC | DEL_REMOTE_MAC_FROM_VTEP);
+ zvni_vtep_del_all(zvni, 1);
+}
+
+/* Cleanup EVPN configuration of a specific VRF */
+static void zebra_evpn_vrf_cfg_cleanup(struct zebra_vrf *zvrf)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zvrf->advertise_all_vni = 0;
+ zvrf->advertise_gw_macip = 0;
+ zvrf->advertise_svi_macip = 0;
+ zvrf->vxlan_flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL;
+
+ hash_iterate(zvrf->vni_table, zvni_evpn_cfg_cleanup, NULL);
+
+ if (zvrf->l3vni)
+ zl3vni = zl3vni_lookup(zvrf->l3vni);
+ if (zl3vni) {
+ /* delete and uninstall all rmacs */
+ hash_iterate(zl3vni->rmac_table, zl3vni_del_rmac_hash_entry,
+ zl3vni);
+ /* delete and uninstall all next-hops */
+ hash_iterate(zl3vni->nh_table, zl3vni_del_nh_hash_entry,
+ zl3vni);
+ }
+}
+
+/* Cleanup BGP EVPN configuration upon client disconnect */
+static int zebra_evpn_bgp_cfg_clean_up(struct zserv *client)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+
+ RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+ zvrf = vrf->info;
+ if (zvrf)
+ zebra_evpn_vrf_cfg_cleanup(zvrf);
+ }
+
+ return 0;
+}
+
+static int zebra_evpn_pim_cfg_clean_up(struct zserv *client)
+{
+ struct zebra_vrf *zvrf = zebra_vrf_get_evpn();
+
+ if (CHECK_FLAG(zvrf->flags, ZEBRA_PIM_SEND_VXLAN_SG)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("VxLAN SG updates to PIM, stop");
+ UNSET_FLAG(zvrf->flags, ZEBRA_PIM_SEND_VXLAN_SG);
+ }
+
+ return 0;
+}
+
+static int zebra_evpn_cfg_clean_up(struct zserv *client)
+{
+ if (client->proto == ZEBRA_ROUTE_BGP)
+ return zebra_evpn_bgp_cfg_clean_up(client);
+
+ if (client->proto == ZEBRA_ROUTE_PIM)
+ return zebra_evpn_pim_cfg_clean_up(client);
+
+ return 0;
+}
+
+/* Cleanup BGP EVPN configuration upon client disconnect */
+extern void zebra_evpn_init(void)
+{
+ hook_register(zserv_client_close, zebra_evpn_cfg_clean_up);
+}