From a6db1c14a434a2f2e389de2ef871ed4a3d10d30a Mon Sep 17 00:00:00 2001 From: Igor Ryzhov Date: Mon, 22 Jan 2024 21:34:35 +0200 Subject: [PATCH] zebra: convert interface evpn mh es-id commands to NB Signed-off-by: Igor Ryzhov --- lib/yang_wrappers.c | 7 ++ lib/yang_wrappers.h | 2 + yang/frr-zebra.yang | 29 +++++ zebra/subdir.am | 2 +- zebra/zebra_evpn_mh.c | 242 ++++++++++++++-------------------------- zebra/zebra_evpn_mh.h | 6 + zebra/zebra_nb.c | 21 ++++ zebra/zebra_nb.h | 11 ++ zebra/zebra_nb_config.c | 200 +++++++++++++++++++++++++++++++++ 9 files changed, 361 insertions(+), 159 deletions(-) diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index 237913880a..a0133954c3 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -1019,6 +1019,13 @@ void yang_str2mac(const char *value, struct ethaddr *mac) (void)prefix_str2mac(value, mac); } +void yang_dnode_get_mac(struct ethaddr *mac, const struct lyd_node *dnode, + const char *xpath_fmt, ...) +{ + const char *canon = YANG_DNODE_XPATH_GET_CANON(dnode, xpath_fmt); + (void)prefix_str2mac(canon, mac); +} + struct yang_data *yang_data_new_date_and_time(const char *xpath, time_t time) { struct tm tm; diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h index 4fb33542b3..59b5b13acd 100644 --- a/lib/yang_wrappers.h +++ b/lib/yang_wrappers.h @@ -195,6 +195,8 @@ extern void yang_get_default_ip(struct ipaddr *var, const char *xpath_fmt, ...) extern struct yang_data *yang_data_new_mac(const char *xpath, const struct ethaddr *mac); extern void yang_str2mac(const char *value, struct ethaddr *mac); +extern void yang_dnode_get_mac(struct ethaddr *mac, const struct lyd_node *dnode, + const char *xpath_fmt, ...) PRINTFRR(3, 4); /*data-and-time */ extern struct yang_data *yang_data_new_date_and_time(const char *xpath, diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang index d876c90bf7..e124840639 100644 --- a/yang/frr-zebra.yang +++ b/yang/frr-zebra.yang @@ -2217,6 +2217,35 @@ module frr-zebra { // TODO -- other link-params options // for (experimental/partial TE use in IGP extensions) } + container evpn-mh { + description "EVPN multihoming configuration"; + choice esi-choice { + description "ESI type"; + container type-0 { + leaf esi { + type yang:hex-string { + length "29"; + } + description + "10-octet ESI."; + } + } + container type-3 { + leaf system-mac { + type yang:mac-address; + description + "System MAC address."; + } + leaf local-discriminator { + type uint32 { + range "1..16777215"; + } + description + "Local discriminator."; + } + } + } + } container state { config false; description diff --git a/zebra/subdir.am b/zebra/subdir.am index a59515d3a8..6d499e97e8 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -28,7 +28,7 @@ man8 += $(MANBUILD)/frr-zebra.8 ## endif ZEBRA endif -zebra_zebra_LDADD = lib/libfrr.la $(LIBCAP) $(UST_LIBS) +zebra_zebra_LDADD = lib/libfrr.la $(LIBCAP) $(LIBYANG_LIBS) $(UST_LIBS) if HAVE_PROTOBUF3 zebra_zebra_LDADD += mlag/libmlag_pb.la $(PROTOBUF_C_LIBS) zebra/zebra_mlag.$(OBJEXT): mlag/mlag.pb-c.h diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index a5092c629a..24542932fe 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -20,6 +20,7 @@ #include "table.h" #include "vlan.h" #include "vxlan.h" +#include "northbound_cli.h" #include "zebra/zebra_router.h" #include "zebra/debug.h" @@ -52,7 +53,7 @@ static void zebra_evpn_es_get_one_base_evpn(void); static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, struct zebra_evpn *zevpn, bool add); static void zebra_evpn_local_es_del(struct zebra_evpn_es **esp); -static int zebra_evpn_local_es_update(struct zebra_if *zif, esi_t *esi); +static void zebra_evpn_local_es_update(struct zebra_if *zif); 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); @@ -1139,7 +1140,7 @@ void zebra_evpn_if_init(struct zebra_if *zif) /* if an es_id and sysmac are already present against the interface * activate it */ - zebra_evpn_local_es_update(zif, &zif->es_info.esi); + zebra_evpn_local_es_update(zif); } /* handle deletion of an access port by removing it from all associated @@ -2402,73 +2403,63 @@ static void zebra_evpn_es_remote_info_re_eval(struct zebra_evpn_es **esp) } } +void zebra_build_type3_esi(uint32_t lid, struct ethaddr *mac, esi_t *esi) +{ + int offset = 0; + int field_bytes = 0; + + /* build 10-byte type-3-ESI - + * Type(1-byte), MAC(6-bytes), ES-LID (3-bytes) + */ + field_bytes = 1; + esi->val[offset] = ESI_TYPE_MAC; + offset += field_bytes; + + field_bytes = ETH_ALEN; + memcpy(&esi->val[offset], (uint8_t *)mac, field_bytes); + offset += field_bytes; + + esi->val[offset++] = (uint8_t)(lid >> 16); + esi->val[offset++] = (uint8_t)(lid >> 8); + esi->val[offset++] = (uint8_t)lid; +} + /* A new local es is created when a local-es-id and sysmac is configured * against an interface. */ -static int zebra_evpn_local_es_update(struct zebra_if *zif, esi_t *esi) +static void zebra_evpn_local_es_update(struct zebra_if *zif) { struct zebra_evpn_es *old_es = zif->es_info.es; struct zebra_evpn_es *es; + esi_t _esi, *esi; + + if (!zebra_evpn_is_if_es_capable(zif)) + return; + + if (memcmp(&zif->es_info.esi, zero_esi, sizeof(*zero_esi))) { + esi = &zif->es_info.esi; + } else if (zif->es_info.lid && !is_zero_mac(&zif->es_info.sysmac)) { + zebra_build_type3_esi(zif->es_info.lid, &zif->es_info.sysmac, + &_esi); + esi = &_esi; + } else { + esi = zero_esi; + } if (old_es && !memcmp(&old_es->esi, esi, sizeof(*esi))) /* dup - nothing to be done */ - return 0; + return; /* release the old_es against the zif */ if (old_es) zebra_evpn_local_es_del(&old_es); es = zebra_evpn_es_find(esi); - if (es) { - /* if it exists against another interface flag an error */ - if (es->zif && es->zif != zif) - return -1; - } else { - /* create new es */ + if (!es) es = zebra_evpn_es_new(esi); - } - memcpy(&zif->es_info.esi, esi, sizeof(*esi)); if (es) zebra_evpn_es_local_info_set(es, zif); - - return 0; -} - -static int zebra_evpn_type3_esi_update(struct zebra_if *zif, uint32_t lid, - struct ethaddr *sysmac) -{ - struct zebra_evpn_es *old_es = zif->es_info.es; - esi_t esi; - int offset = 0; - int field_bytes = 0; - - /* Complete config of the ES-ID bootstraps the ES */ - if (!lid || is_zero_mac(sysmac)) { - /* clear old esi */ - memset(&zif->es_info.esi, 0, sizeof(zif->es_info.esi)); - /* if in ES is attached to zif delete it */ - if (old_es) - zebra_evpn_local_es_del(&old_es); - return 0; - } - - /* build 10-byte type-3-ESI - - * Type(1-byte), MAC(6-bytes), ES-LID (3-bytes) - */ - field_bytes = 1; - esi.val[offset] = ESI_TYPE_MAC; - offset += field_bytes; - - field_bytes = ETH_ALEN; - memcpy(&esi.val[offset], (uint8_t *)sysmac, field_bytes); - offset += field_bytes; - - esi.val[offset++] = (uint8_t)(lid >> 16); - esi.val[offset++] = (uint8_t)(lid >> 8); - esi.val[offset++] = (uint8_t)lid; - - return zebra_evpn_local_es_update(zif, &esi); } int zebra_evpn_remote_es_del(const esi_t *esi, struct in_addr vtep_ip) @@ -2673,44 +2664,33 @@ static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, } /* sysmac part of a local ESI has changed */ -static int zebra_evpn_es_sys_mac_update(struct zebra_if *zif, - struct ethaddr *sysmac) +void zebra_evpn_es_sys_mac_update(struct zebra_if *zif, struct ethaddr *sysmac) { - int rv; - - rv = zebra_evpn_type3_esi_update(zif, zif->es_info.lid, sysmac); - if (!rv) + if (sysmac) memcpy(&zif->es_info.sysmac, sysmac, sizeof(struct ethaddr)); + else + memset(&zif->es_info.sysmac, 0, sizeof(struct ethaddr)); - return rv; + zebra_evpn_local_es_update(zif); } /* local-ID part of ESI has changed */ -static int zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid) +void zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid) { - int rv; - - rv = zebra_evpn_type3_esi_update(zif, lid, &zif->es_info.sysmac); - if (!rv) - zif->es_info.lid = lid; + zif->es_info.lid = lid; - return rv; + zebra_evpn_local_es_update(zif); } /* type-0 esi has changed */ -static int zebra_evpn_es_type0_esi_update(struct zebra_if *zif, esi_t *esi) +void zebra_evpn_es_type0_esi_update(struct zebra_if *zif, esi_t *esi) { - int rv; - - rv = zebra_evpn_local_es_update(zif, esi); - - /* clear the old es_lid, es_sysmac - type-0 is being set so old - * type-3 params need to be flushed - */ - memset(&zif->es_info.sysmac, 0, sizeof(struct ethaddr)); - zif->es_info.lid = 0; + if (esi) + memcpy(&zif->es_info.esi, esi, sizeof(*esi)); + else + memset(&zif->es_info.esi, 0, sizeof(*esi)); - return rv; + zebra_evpn_local_es_update(zif); } void zebra_evpn_es_cleanup(void) @@ -3409,9 +3389,9 @@ DEFPY(zebra_evpn_es_pref, zebra_evpn_es_pref_cmd, } /* CLI for setting up sysmac part of ESI on an access port */ -DEFPY(zebra_evpn_es_sys_mac, +DEFPY_YANG (zebra_evpn_es_sys_mac, zebra_evpn_es_sys_mac_cmd, - "[no$no] evpn mh es-sys-mac [X:X:X:X:X:X$mac]", + "[no$no] evpn mh es-sys-mac ![X:X:X:X:X:X$mac]", NO_STR "EVPN\n" EVPN_MH_VTY_STR @@ -3419,47 +3399,21 @@ DEFPY(zebra_evpn_es_sys_mac, MAC_STR ) { - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif; - int ret = 0; - - zif = ifp->info; - - if (no) { - static struct ethaddr zero_mac; - - ret = zebra_evpn_es_sys_mac_update(zif, &zero_mac); - if (ret == -1) { - vty_out(vty, "%% Failed to clear ES sysmac\n"); - return CMD_WARNING; - } - } else { - - if (!zebra_evpn_is_if_es_capable(zif)) { - vty_out(vty, - "%% ESI cannot be associated with this interface type\n"); - return CMD_WARNING; - } - - if (!mac || is_zero_mac(&mac->eth_addr)) { - vty_out(vty, "%% ES sysmac value is invalid\n"); - return CMD_WARNING; - } - - ret = zebra_evpn_es_sys_mac_update(zif, &mac->eth_addr); - if (ret == -1) { - vty_out(vty, - "%% ESI already exists on a different interface\n"); - return CMD_WARNING; - } - } - return CMD_SUCCESS; + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-3/system-mac", + NB_OP_MODIFY, mac_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-3/system-mac", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } /* CLI for setting up local-ID part of ESI on an access port */ -DEFPY(zebra_evpn_es_id, +DEFPY_YANG (zebra_evpn_es_id, zebra_evpn_es_id_cmd, - "[no$no] evpn mh es-id [(1-16777215)$es_lid | NAME$esi_str]", + "[no$no] evpn mh es-id ![(1-16777215)$es_lid | NAME$esi_str]", NO_STR "EVPN\n" EVPN_MH_VTY_STR @@ -3468,53 +3422,25 @@ DEFPY(zebra_evpn_es_id, "10-byte ID - 00:AA:BB:CC:DD:EE:FF:GG:HH:II\n" ) { - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif; - int ret = 0; - esi_t esi; - - zif = ifp->info; - if (no) { - if (zif->es_info.lid) - ret = zebra_evpn_es_lid_update(zif, 0); - else if (memcmp(&zif->es_info.esi, zero_esi, sizeof(*zero_esi))) - ret = zebra_evpn_es_type0_esi_update(zif, zero_esi); - - if (ret == -1) { - vty_out(vty, - "%% Failed to clear ES local id or ESI name\n"); - return CMD_WARNING; - } + /* We don't know which one is configured, so detroy both types. */ + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-0/esi", + NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-3/local-discriminator", + NB_OP_DESTROY, NULL); } else { - if (!zebra_evpn_is_if_es_capable(zif)) { - vty_out(vty, - "%% ESI cannot be associated with this interface type\n"); - return CMD_WARNING; - } - - if (esi_str) { - if (!str_to_esi(esi_str, &esi)) { - vty_out(vty, "%% Malformed ESI name\n"); - return CMD_WARNING; - } - ret = zebra_evpn_es_type0_esi_update(zif, &esi); - } else { - if (!es_lid) { - vty_out(vty, - "%% Specify ES local id or ESI name\n"); - return CMD_WARNING; - } - ret = zebra_evpn_es_lid_update(zif, es_lid); - } - - if (ret == -1) { - vty_out(vty, - "%% ESI already exists on a different interface\n"); - return CMD_WARNING; - } + if (esi_str) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-0/esi", + NB_OP_MODIFY, esi_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-3/local-discriminator", + NB_OP_MODIFY, es_lid_str); } - return CMD_SUCCESS; + return nb_cli_apply_changes(vty, NULL); } /* CLI for tagging an interface as an uplink */ diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 59a41d0606..ebfe66f9dc 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -382,4 +382,10 @@ extern void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS); extern struct zebra_evpn_es_evi * zebra_evpn_es_evi_find(struct zebra_evpn_es *es, struct zebra_evpn *zevpn); +void zebra_build_type3_esi(uint32_t lid, struct ethaddr *mac, esi_t *esi); + +void zebra_evpn_es_sys_mac_update(struct zebra_if *zif, struct ethaddr *sysmac); +void zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid); +void zebra_evpn_es_type0_esi_update(struct zebra_if *zif, esi_t *esi); + #endif /* _ZEBRA_EVPN_MH_H */ diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c index 3952e9cb1e..d5f0450fb0 100644 --- a/zebra/zebra_nb.c +++ b/zebra/zebra_nb.c @@ -507,6 +507,27 @@ const struct frr_yang_module_info frr_zebra_info = { .destroy = lib_interface_zebra_link_params_packet_loss_destroy, } }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-0/esi", + .cbs = { + .modify = lib_interface_zebra_evpn_mh_type_0_esi_modify, + .destroy = lib_interface_zebra_evpn_mh_type_0_esi_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/system-mac", + .cbs = { + .modify = lib_interface_zebra_evpn_mh_type_3_system_mac_modify, + .destroy = lib_interface_zebra_evpn_mh_type_3_system_mac_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/local-discriminator", + .cbs = { + .modify = lib_interface_zebra_evpn_mh_type_3_local_discriminator_modify, + .destroy = lib_interface_zebra_evpn_mh_type_3_local_discriminator_destroy, + } + }, { .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/state/up-count", .cbs = { diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h index 9d4e720c82..6494cec9e6 100644 --- a/zebra/zebra_nb.h +++ b/zebra/zebra_nb.h @@ -166,6 +166,17 @@ int lib_interface_zebra_link_params_packet_loss_modify( struct nb_cb_modify_args *args); int lib_interface_zebra_link_params_packet_loss_destroy( struct nb_cb_destroy_args *args); +int lib_interface_zebra_evpn_mh_type_0_esi_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_evpn_mh_type_0_esi_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_evpn_mh_type_3_system_mac_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_evpn_mh_type_3_system_mac_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_evpn_mh_type_3_local_discriminator_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_evpn_mh_type_3_local_discriminator_destroy( + struct nb_cb_destroy_args *args); struct yang_data * lib_interface_zebra_state_up_count_get_elem(struct nb_cb_get_elem_args *args); struct yang_data * diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c index 844232935a..535b60f7f0 100644 --- a/zebra/zebra_nb_config.c +++ b/zebra/zebra_nb_config.c @@ -23,6 +23,7 @@ #include "zebra/debug.h" #include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn_mh.h" /* * XPath: /frr-zebra:zebra/mcast-rpf-lookup @@ -2200,6 +2201,205 @@ int lib_interface_zebra_link_params_packet_loss_destroy( return NB_OK; } +static bool evpn_mh_dnode_to_esi(const struct lyd_node *dnode, esi_t *esi) +{ + if (yang_dnode_exists(dnode, "type-0/esi")) { + str_to_esi(yang_dnode_get_string(dnode, "type-0/esi"), esi); + } else if (yang_dnode_exists(dnode, "type-3/system-mac") && + yang_dnode_exists(dnode, "type-3/local-discriminator")) { + struct ethaddr mac; + uint32_t lid; + + yang_dnode_get_mac(&mac, dnode, "type-3/system-mac"); + lid = yang_dnode_get_uint32(dnode, "type-3/local-discriminator"); + + zebra_build_type3_esi(lid, &mac, esi); + } else { + return false; + } + + return true; +} + +struct esi_cmp_iter_arg { + struct lyd_node *dnode; + esi_t esi; + bool exists; +}; + +static int esi_cmp_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct esi_cmp_iter_arg *iter = arg; + esi_t esi; + + if (dnode == iter->dnode) + return YANG_ITER_CONTINUE; + + if (!evpn_mh_dnode_to_esi(dnode, &esi)) + return YANG_ITER_CONTINUE; + + if (!memcmp(&esi, &iter->esi, ESI_BYTES)) { + iter->exists = true; + return YANG_ITER_STOP; + } + + return YANG_ITER_CONTINUE; +} + +/* evpn-mh should be passed to this function */ +static bool esi_unique(struct lyd_node *dnode) +{ + struct esi_cmp_iter_arg iter; + + iter.dnode = dnode; + evpn_mh_dnode_to_esi(dnode, &iter.esi); + iter.exists = false; + + yang_dnode_iterate(esi_cmp_iter_cb, &iter, dnode, + "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh"); + + if (iter.exists) + return false; + + return true; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-0/esi + */ +int lib_interface_zebra_evpn_mh_type_0_esi_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + esi_t esi; + + switch (args->event) { + case NB_EV_VALIDATE: + if (!esi_unique(lyd_parent(lyd_parent(args->dnode)))) { + snprintfrr(args->errmsg, args->errmsg_len, + "ESI already exists on a different interface"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + str_to_esi(yang_dnode_get_string(args->dnode, NULL), &esi); + zebra_evpn_es_type0_esi_update(ifp->info, &esi); + break; + } + + return NB_OK; +} + +int lib_interface_zebra_evpn_mh_type_0_esi_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zebra_evpn_es_type0_esi_update(ifp->info, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/system-mac + */ +int lib_interface_zebra_evpn_mh_type_3_system_mac_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct ethaddr mac; + + yang_dnode_get_mac(&mac, args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + if (is_zero_mac(&mac)) { + snprintfrr(args->errmsg, args->errmsg_len, + "MAC cannot be all-zeroes"); + return NB_ERR_VALIDATION; + } + if (!esi_unique(lyd_parent(lyd_parent(args->dnode)))) { + snprintfrr(args->errmsg, args->errmsg_len, + "ESI already exists on a different interface"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + zebra_evpn_es_sys_mac_update(ifp->info, &mac); + break; + } + + return NB_OK; +} + +int lib_interface_zebra_evpn_mh_type_3_system_mac_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zebra_evpn_es_sys_mac_update(ifp->info, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/local-discriminator + */ +int lib_interface_zebra_evpn_mh_type_3_local_discriminator_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + uint32_t lid; + + switch (args->event) { + case NB_EV_VALIDATE: + if (!esi_unique(lyd_parent(lyd_parent(args->dnode)))) { + snprintfrr(args->errmsg, args->errmsg_len, + "ESI already exists on a different interface"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + lid = yang_dnode_get_uint32(args->dnode, NULL); + zebra_evpn_es_lid_update(ifp->info, lid); + break; + } + + return NB_OK; +} + +int lib_interface_zebra_evpn_mh_type_3_local_discriminator_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zebra_evpn_es_lid_update(ifp->info, 0); + + return NB_OK; +} + /* * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id */ -- 2.39.5